fix: Shipping toggle refresh + AlertDialog + Local Pickup nav + Notifications info

## 1. Fixed Shipping Method Toggle State 
- Updated useEffect to properly sync selectedZone with zones data
- Added JSON comparison to prevent infinite loops
- Toggle now refreshes zone data correctly

## 2. Replace confirm() with AlertDialog 
- Added AlertDialog component for delete confirmation
- Shows method name in confirmation message
- Better UX with proper dialog styling
- Updated both desktop and mobile versions

## 3. Added Local Pickup to Navigation 
- Added "Local Pickup" menu item in Settings
- Now accessible from Settings > Local Pickup
- Path: /settings/local-pickup

## 4. Shipping Cost Shortcodes 
- Already supported via HTML rendering
- WooCommerce shortcodes like [fee percent="10"] work
- [qty], [cost] are handled by WooCommerce backend
- No additional SPA work needed

## 5. Enhanced Notifications Page 
- Added comprehensive info card explaining:
  - What WooNooW provides (simple toggle)
  - What WooCommerce provides (advanced config)
- Clear guidance on when to use each
- Links to WooCommerce for templates/styling
- Replaced ToggleField with Switch for simpler usage

## Key Decisions:
 AlertDialog > confirm() for better UX
 Notifications = Simple toggle + guidance to WC
 Shortcodes handled by WooCommerce (no SPA work)
 Local Pickup now discoverable in nav
This commit is contained in:
dwindown
2025-11-09 23:56:34 +07:00
parent 5fb5eda9c3
commit a373b141b7
3 changed files with 80 additions and 14 deletions

View File

@@ -111,6 +111,7 @@ function getStaticFallbackTree(): MainNode[] {
{ label: 'Store Details', mode: 'spa' as const, path: '/settings/store' }, { label: 'Store Details', mode: 'spa' as const, path: '/settings/store' },
{ label: 'Payments', mode: 'spa' as const, path: '/settings/payments' }, { label: 'Payments', mode: 'spa' as const, path: '/settings/payments' },
{ label: 'Shipping & Delivery', mode: 'spa' as const, path: '/settings/shipping' }, { label: 'Shipping & Delivery', mode: 'spa' as const, path: '/settings/shipping' },
{ label: 'Local Pickup', mode: 'spa' as const, path: '/settings/local-pickup' },
{ label: 'Tax', mode: 'spa' as const, path: '/settings/tax' }, { label: 'Tax', mode: 'spa' as const, path: '/settings/tax' },
{ label: 'Checkout', mode: 'spa' as const, path: '/settings/checkout' }, { label: 'Checkout', mode: 'spa' as const, path: '/settings/checkout' },
{ label: 'Customer Accounts', mode: 'spa' as const, path: '/settings/customers' }, { label: 'Customer Accounts', mode: 'spa' as const, path: '/settings/customers' },

View File

@@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api'; import { api } from '@/lib/api';
import { SettingsLayout } from './components/SettingsLayout'; import { SettingsLayout } from './components/SettingsLayout';
import { SettingsCard } from './components/SettingsCard'; import { SettingsCard } from './components/SettingsCard';
import { ToggleField } from './components/ToggleField'; import { Switch } from '@/components/ui/switch';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ExternalLink, RefreshCw, Mail } from 'lucide-react'; import { ExternalLink, RefreshCw, Mail } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -65,6 +65,38 @@ export default function NotificationsSettings() {
} }
> >
<div className="space-y-6"> <div className="space-y-6">
{/* Info Card */}
<SettingsCard
title={__('About Email Notifications')}
description={__('Quick enable/disable controls for WooCommerce emails')}
>
<div className="text-sm text-muted-foreground space-y-2">
<p>
{__('WooNooW provides simple toggle controls to enable or disable email notifications. For advanced customization like email templates, styling, content, and recipients, use the WooCommerce settings page.')}
</p>
<p className="font-medium text-foreground">
{__('What you can do here:')}
</p>
<ul className="list-disc list-inside space-y-1 ml-2">
<li>{__('Enable/disable customer emails (order confirmations, shipping updates, etc.)')}</li>
<li>{__('Enable/disable admin emails (new order notifications, low stock alerts, etc.)')}</li>
<li>{__('View current sender name and email address')}</li>
</ul>
<p className="font-medium text-foreground mt-3">
{__('For advanced configuration:')}
</p>
<ul className="list-disc list-inside space-y-1 ml-2">
<li>{__('Email templates and HTML/CSS styling')}</li>
<li>{__('Email subject lines and content')}</li>
<li>{__('Custom recipient addresses')}</li>
<li>{__('Additional email headers')}</li>
</ul>
<p className="mt-3">
{__('Use the "Edit in WooCommerce" links below or the advanced settings link at the bottom.')}
</p>
</div>
</SettingsCard>
{/* Customer Emails */} {/* Customer Emails */}
<SettingsCard <SettingsCard
title={__('Customer Notifications')} title={__('Customer Notifications')}
@@ -81,9 +113,9 @@ export default function NotificationsSettings() {
<p className="text-sm text-muted-foreground">{email.description}</p> <p className="text-sm text-muted-foreground">{email.description}</p>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ToggleField <Switch
checked={email.enabled === 'yes'} checked={email.enabled === 'yes'}
onChange={(checked) => toggleMutation.mutate({ onCheckedChange={(checked) => toggleMutation.mutate({
emailId: email.id, emailId: email.id,
enabled: checked enabled: checked
})} })}
@@ -124,9 +156,9 @@ export default function NotificationsSettings() {
<p className="text-sm text-muted-foreground">{email.description}</p> <p className="text-sm text-muted-foreground">{email.description}</p>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ToggleField <Switch
checked={email.enabled === 'yes'} checked={email.enabled === 'yes'}
onChange={(checked) => toggleMutation.mutate({ onCheckedChange={(checked) => toggleMutation.mutate({
emailId: email.id, emailId: email.id,
enabled: checked enabled: checked
})} })}

View File

@@ -6,6 +6,7 @@ import { SettingsCard } from './components/SettingsCard';
import { ToggleField } from './components/ToggleField'; import { ToggleField } from './components/ToggleField';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog';
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer'; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
import { Globe, Truck, MapPin, Edit, Trash2, RefreshCw, Loader2, ExternalLink, Settings, Plus, X, ChevronDown } from 'lucide-react'; import { Globe, Truck, MapPin, Edit, Trash2, RefreshCw, Loader2, ExternalLink, Settings, Plus, X, ChevronDown } from 'lucide-react';
@@ -33,10 +34,10 @@ export default function ShippingPage() {
const wcAdminUrl = (window as any).WNW_CONFIG?.wpAdminUrl || '/wp-admin'; const wcAdminUrl = (window as any).WNW_CONFIG?.wpAdminUrl || '/wp-admin';
const [togglingMethod, setTogglingMethod] = useState<string | null>(null); const [togglingMethod, setTogglingMethod] = useState<string | null>(null);
const [selectedZone, setSelectedZone] = useState<any | null>(null); const [selectedZone, setSelectedZone] = useState<any | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [showAddMethod, setShowAddMethod] = useState(false); const [showAddMethod, setShowAddMethod] = useState(false);
const [expandedMethod, setExpandedMethod] = useState<string>(''); const [expandedMethod, setExpandedMethod] = useState<string>('');
const [methodSettings, setMethodSettings] = useState<Record<string, any>>({}); const [methodSettings, setMethodSettings] = useState<Record<string, any>>({});
const [deletingMethod, setDeletingMethod] = useState<{ zoneId: number; instanceId: number; name: string } | null>(null);
const isDesktop = useMediaQuery("(min-width: 768px)"); const isDesktop = useMediaQuery("(min-width: 768px)");
// Fetch shipping zones from WooCommerce // Fetch shipping zones from WooCommerce
@@ -55,13 +56,13 @@ export default function ShippingPage() {
// Sync selectedZone with zones data when it changes // Sync selectedZone with zones data when it changes
useEffect(() => { useEffect(() => {
if (selectedZone && zones.length > 0) { if (selectedZone && zones && zones.length > 0) {
const updatedZone = zones.find((z: any) => z.id === selectedZone.id); const updatedZone = zones.find((z: any) => z.id === selectedZone.id);
if (updatedZone) { if (updatedZone && JSON.stringify(updatedZone) !== JSON.stringify(selectedZone)) {
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
} }
} }
}, [zones]); }, [zones, selectedZone]);
// Toggle shipping method mutation // Toggle shipping method mutation
const toggleMutation = useMutation({ const toggleMutation = useMutation({
@@ -153,9 +154,19 @@ export default function ShippingPage() {
} }
}; };
const handleDeleteMethod = (instanceId: number) => { const handleDeleteMethod = (instanceId: number, methodName: string) => {
if (selectedZone && confirm(__('Are you sure you want to delete this shipping method?'))) { if (selectedZone) {
deleteMethodMutation.mutate({ zoneId: selectedZone.id, instanceId }); setDeletingMethod({ zoneId: selectedZone.id, instanceId, name: methodName });
}
};
const confirmDelete = () => {
if (deletingMethod) {
deleteMethodMutation.mutate({
zoneId: deletingMethod.zoneId,
instanceId: deletingMethod.instanceId
});
setDeletingMethod(null);
} }
}; };
@@ -424,7 +435,7 @@ export default function ShippingPage() {
variant="destructive" variant="destructive"
size="sm" size="sm"
onClick={() => { onClick={() => {
handleDeleteMethod(rate.instance_id); handleDeleteMethod(rate.instance_id, rate.title);
setExpandedMethod(''); setExpandedMethod('');
}} }}
disabled={deleteMethodMutation.isPending} disabled={deleteMethodMutation.isPending}
@@ -594,7 +605,7 @@ export default function ShippingPage() {
variant="destructive" variant="destructive"
size="sm" size="sm"
onClick={() => { onClick={() => {
handleDeleteMethod(rate.instance_id); handleDeleteMethod(rate.instance_id, rate.title);
setExpandedMethod(''); setExpandedMethod('');
}} }}
disabled={deleteMethodMutation.isPending} disabled={deleteMethodMutation.isPending}
@@ -696,6 +707,28 @@ export default function ShippingPage() {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{/* Delete Confirmation Dialog */}
<AlertDialog open={!!deletingMethod} onOpenChange={() => setDeletingMethod(null)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{__('Delete Shipping Method?')}</AlertDialogTitle>
<AlertDialogDescription>
{__('Are you sure you want to delete')} <strong>{deletingMethod?.name}</strong>?
{' '}{__('This action cannot be undone.')}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{__('Cancel')}</AlertDialogCancel>
<AlertDialogAction
onClick={confirmDelete}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{__('Delete')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</SettingsLayout> </SettingsLayout>
); );
} }