fix: Reactive store name in header + sticky header positioning
1. Store Name Updates in Header ✅ Problem: Changing store name doesn't update topbar title Solution: Custom event system Flow: - User saves store settings - Dispatch 'woonoow:store:updated' event with store_name - Header component listens for event - Updates title in real-time Files: - App.tsx: useState + useEffect listener - Store.tsx: Dispatch event on save success 2. Sticky Header Positioning ✅ Problem 1: Sticky header hidden under submenu Solution: top-[49px] instead of top-0 Problem 2: Sticky header not edge-to-edge Solution: Negative margins to break out of container Before: <div className="sticky top-0 ..."> <div className="container ..."> After: <div className="sticky top-[49px] -mx-4 px-4 lg:-mx-6 lg:px-6"> <div className="container ..."> Responsive: - Mobile: -mx-4 px-4 (breaks out of 16px padding) - Desktop: -mx-6 px-6 (breaks out of 24px padding) Result: ✅ Sticky header below submenu (49px offset) ✅ Edge-to-edge background ✅ Content still centered in container ✅ Works in fullscreen, standalone, and wp-admin modes 3. Layout Structure Parent: space-y-6 lg:p-6 pb-6 ├─ Sticky Header: -mx to break out, top-[49px] └─ Content: container max-w-5xl This ensures: - Sticky header spans full width - Content stays centered - Proper spacing maintained Files Modified: - App.tsx: Reactive site title - Store.tsx: Dispatch update event - SettingsLayout.tsx: Fixed sticky positioning
This commit is contained in:
@@ -257,9 +257,21 @@ function AddonRoute({ config }: { config: any }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Header({ onFullscreen, fullscreen, showToggle = true }: { onFullscreen: () => void; fullscreen: boolean; showToggle?: boolean }) {
|
function Header({ onFullscreen, fullscreen, showToggle = true }: { onFullscreen: () => void; fullscreen: boolean; showToggle?: boolean }) {
|
||||||
const siteTitle = (window as any).wnw?.siteTitle || 'WooNooW';
|
const [siteTitle, setSiteTitle] = React.useState((window as any).wnw?.siteTitle || 'WooNooW');
|
||||||
const isStandalone = window.WNW_CONFIG?.standaloneMode ?? false;
|
const isStandalone = window.WNW_CONFIG?.standaloneMode ?? false;
|
||||||
|
|
||||||
|
// Listen for store settings updates
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handleStoreUpdate = (event: CustomEvent) => {
|
||||||
|
if (event.detail?.store_name) {
|
||||||
|
setSiteTitle(event.detail.store_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('woonoow:store:updated' as any, handleStoreUpdate);
|
||||||
|
return () => window.removeEventListener('woonoow:store:updated' as any, handleStoreUpdate);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
await fetch((window.WNW_CONFIG?.restUrl || '') + '/auth/logout', {
|
await fetch((window.WNW_CONFIG?.restUrl || '') + '/auth/logout', {
|
||||||
@@ -406,7 +418,7 @@ function Shell() {
|
|||||||
{isDashboardRoute ? (
|
{isDashboardRoute ? (
|
||||||
<DashboardSubmenuBar items={main.children} fullscreen={true} />
|
<DashboardSubmenuBar items={main.children} fullscreen={true} />
|
||||||
) : (
|
) : (
|
||||||
<SubmenuBar items={main.children} />
|
<SubmenuBar items={main.children} fullscreen={true} />
|
||||||
)}
|
)}
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<AppRoutes />
|
<AppRoutes />
|
||||||
@@ -419,7 +431,7 @@ function Shell() {
|
|||||||
{isDashboardRoute ? (
|
{isDashboardRoute ? (
|
||||||
<DashboardSubmenuBar items={main.children} fullscreen={true} />
|
<DashboardSubmenuBar items={main.children} fullscreen={true} />
|
||||||
) : (
|
) : (
|
||||||
<SubmenuBar items={main.children} />
|
<SubmenuBar items={main.children} fullscreen={true} />
|
||||||
)}
|
)}
|
||||||
<main className="flex-1 p-4 overflow-auto">
|
<main className="flex-1 p-4 overflow-auto">
|
||||||
<AppRoutes />
|
<AppRoutes />
|
||||||
@@ -432,7 +444,7 @@ function Shell() {
|
|||||||
{isDashboardRoute ? (
|
{isDashboardRoute ? (
|
||||||
<DashboardSubmenuBar items={main.children} fullscreen={false} />
|
<DashboardSubmenuBar items={main.children} fullscreen={false} />
|
||||||
) : (
|
) : (
|
||||||
<SubmenuBar items={main.children} />
|
<SubmenuBar items={main.children} fullscreen={false} />
|
||||||
)}
|
)}
|
||||||
<main className="flex-1 p-4 overflow-auto">
|
<main className="flex-1 p-4 overflow-auto">
|
||||||
<AppRoutes />
|
<AppRoutes />
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ export default function Dashboard() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 p-6 pb-6">
|
<div className="space-y-6 lg:p-6 pb-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h1 className="text-2xl font-bold">{__('Dashboard')}</h1>
|
<h1 className="text-2xl font-bold">{__('Dashboard')}</h1>
|
||||||
|
|||||||
@@ -117,26 +117,19 @@ export default function PaymentsPage() {
|
|||||||
// Initialize order from saved order or gateways
|
// Initialize order from saved order or gateways
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (gateways.length > 0 && manualOrder.length === 0 && onlineOrder.length === 0) {
|
if (gateways.length > 0 && manualOrder.length === 0 && onlineOrder.length === 0) {
|
||||||
console.log('Initializing gateway order...');
|
|
||||||
console.log('All gateways:', gateways.map((g: PaymentGateway) => ({ id: g.id, type: g.type })));
|
|
||||||
console.log('Saved order:', savedOrder);
|
|
||||||
|
|
||||||
// Use saved order if available, otherwise use gateway order
|
// Use saved order if available, otherwise use gateway order
|
||||||
if (savedOrder.manual.length > 0) {
|
if (savedOrder.manual.length > 0) {
|
||||||
console.log('Using saved manual order:', savedOrder.manual);
|
|
||||||
setManualOrder(savedOrder.manual);
|
setManualOrder(savedOrder.manual);
|
||||||
} else {
|
} else {
|
||||||
const manual = gateways.filter((g: PaymentGateway) => g.type === 'manual').map((g: PaymentGateway) => g.id);
|
const manual = gateways.filter((g: PaymentGateway) => g.type === 'manual').map((g: PaymentGateway) => g.id);
|
||||||
console.log('Using gateway manual order:', manual);
|
|
||||||
setManualOrder(manual);
|
setManualOrder(manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedOrder.online.length > 0) {
|
if (savedOrder.online.length > 0) {
|
||||||
console.log('Using saved online order:', savedOrder.online);
|
|
||||||
setOnlineOrder(savedOrder.online);
|
setOnlineOrder(savedOrder.online);
|
||||||
} else {
|
} else {
|
||||||
const online = gateways.filter((g: PaymentGateway) => g.type === 'provider' || g.type === 'other').map((g: PaymentGateway) => g.id);
|
const online = gateways.filter((g: PaymentGateway) => g.type === 'provider' || g.type === 'other').map((g: PaymentGateway) => g.id);
|
||||||
console.log('Using gateway online order:', online);
|
|
||||||
setOnlineOrder(online);
|
setOnlineOrder(online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +190,6 @@ export default function PaymentsPage() {
|
|||||||
|
|
||||||
// Save order to backend
|
// Save order to backend
|
||||||
try {
|
try {
|
||||||
console.log('Saving manual order:', newOrder);
|
|
||||||
await api.post('/payments/gateways/order', {
|
await api.post('/payments/gateways/order', {
|
||||||
category: 'manual',
|
category: 'manual',
|
||||||
order: newOrder,
|
order: newOrder,
|
||||||
@@ -225,7 +217,6 @@ export default function PaymentsPage() {
|
|||||||
|
|
||||||
// Save order to backend
|
// Save order to backend
|
||||||
try {
|
try {
|
||||||
console.log('Saving online order:', newOrder);
|
|
||||||
await api.post('/payments/gateways/order', {
|
await api.post('/payments/gateways/order', {
|
||||||
category: 'online',
|
category: 'online',
|
||||||
order: newOrder,
|
order: newOrder,
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ export default function StoreDetailsPage() {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['store-settings'] });
|
queryClient.invalidateQueries({ queryKey: ['store-settings'] });
|
||||||
toast.success('Your store details have been updated successfully.');
|
toast.success('Your store details have been updated successfully.');
|
||||||
|
|
||||||
|
// Dispatch event to update site title in header
|
||||||
|
window.dispatchEvent(new CustomEvent('woonoow:store:updated', {
|
||||||
|
detail: { store_name: settings.storeName }
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
toast.error('Failed to save store settings');
|
toast.error('Failed to save store settings');
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export function SettingsLayout({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="space-y-6 lg:p-6 pb-6">
|
||||||
{/* Sticky Header with Save Button */}
|
{/* Sticky Header with Save Button - Edge to edge */}
|
||||||
{onSave && (
|
{onSave && (
|
||||||
<div className="sticky top-0 z-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
<div className="sticky top-[49px] z-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 -mx-4 px-4 lg:-mx-6 lg:px-6">
|
||||||
<div className="container px-0 max-w-5xl mx-auto py-3 flex items-center justify-between">
|
<div className="container px-0 max-w-5xl mx-auto py-3 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-lg font-semibold">{title}</h1>
|
<h1 className="text-lg font-semibold">{title}</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user