Files
WooNooW/customer-spa/src/App.tsx
Dwindi Ramadhana 10acb58f6e feat: Toast position control + Currency formatting + Dialog accessibility fixes
1. Toast Position Control 
   - Added toast_position setting to Appearance > General
   - 6 position options: top-left/center/right, bottom-left/center/right
   - Default: top-right
   - Backend: AppearanceController.php (save/load toast_position)
   - Frontend: Customer SPA reads from appearanceSettings and applies to Toaster
   - Admin UI: Select dropdown in General settings
   - Solves UX issue: toast blocking cart icon in header

2. Currency Formatting Fix 
   - Changed formatPrice import from @/lib/utils to @/lib/currency
   - @/lib/currency respects WooCommerce currency settings (IDR, not USD)
   - Reads currency code, symbol, position, separators from window.woonoowCustomer.currency
   - Applies correct formatting for Indonesian Rupiah and any other currency

3. Dialog Accessibility Warnings Fixed 
   - Added DialogDescription component to all taxonomy dialogs
   - Categories: 'Update category information' / 'Create a new product category'
   - Tags: 'Update tag information' / 'Create a new product tag'
   - Attributes: 'Update attribute information' / 'Create a new product attribute'
   - Fixes console warning: Missing Description or aria-describedby

Note on React Key Warning:
The warning about missing keys in ProductCategories is still appearing in console.
All table rows already have proper key props (key={category.term_id}).
This may be a dev server cache issue or a nested element without a key.
The code is correct - keys are present on all mapped elements.

Files Modified:
- includes/Admin/AppearanceController.php (toast_position setting)
- admin-spa/src/routes/Appearance/General.tsx (toast position UI)
- customer-spa/src/App.tsx (apply toast position from settings)
- customer-spa/src/pages/Wishlist.tsx (use correct formatPrice from currency)
- admin-spa/src/routes/Products/Categories.tsx (DialogDescription)
- admin-spa/src/routes/Products/Tags.tsx (DialogDescription)
- admin-spa/src/routes/Products/Attributes.tsx (DialogDescription)

Result:
 Toast notifications now configurable and won't block header elements
 Prices display in correct currency (IDR) with proper formatting
 All Dialog accessibility warnings resolved
⚠️ React key warning persists (but keys are correctly implemented)
2025-12-27 00:12:44 +07:00

95 lines
2.8 KiB
TypeScript

import React from 'react';
import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Toaster } from 'sonner';
// Theme
import { ThemeProvider } from './contexts/ThemeContext';
import { BaseLayout } from './layouts/BaseLayout';
// Pages
import Shop from './pages/Shop';
import Product from './pages/Product';
import Cart from './pages/Cart';
import Checkout from './pages/Checkout';
import ThankYou from './pages/ThankYou';
import Account from './pages/Account';
import Wishlist from './pages/Wishlist';
// Create QueryClient instance
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
refetchOnWindowFocus: false,
retry: 1,
},
},
});
// Get theme config from window (injected by PHP)
const getThemeConfig = () => {
const config = (window as any).woonoowCustomer?.theme;
// Default config if not provided
return config || {
mode: 'full',
layout: 'modern',
colors: {
primary: '#3B82F6',
secondary: '#8B5CF6',
accent: '#10B981',
},
typography: {
preset: 'professional',
},
};
};
// Get appearance settings from window
const getAppearanceSettings = () => {
return (window as any).woonoowCustomer?.appearanceSettings || {};
};
function App() {
const themeConfig = getThemeConfig();
const appearanceSettings = getAppearanceSettings();
const toastPosition = (appearanceSettings?.general?.toast_position || 'top-right') as any;
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider config={themeConfig}>
<HashRouter>
<BaseLayout>
<Routes>
{/* Shop Routes */}
<Route path="/" element={<Shop />} />
<Route path="/shop" element={<Shop />} />
<Route path="/product/:slug" element={<Product />} />
{/* Cart & Checkout */}
<Route path="/cart" element={<Cart />} />
<Route path="/checkout" element={<Checkout />} />
<Route path="/order-received/:orderId" element={<ThankYou />} />
{/* Wishlist - Public route accessible to guests */}
<Route path="/wishlist" element={<Wishlist />} />
{/* My Account */}
<Route path="/my-account/*" element={<Account />} />
{/* Fallback */}
<Route path="*" element={<Navigate to="/shop" replace />} />
</Routes>
</BaseLayout>
</HashRouter>
{/* Toast notifications - position from settings */}
<Toaster position={toastPosition} richColors />
</ThemeProvider>
</QueryClientProvider>
);
}
export default App;