feat: Merge Templates tab into Events tab with toggle + gear icon pattern

 UI Restructuring:
- Removed Templates tab from Staff and Customer pages
- Merged template editing into Events tab
- Changed from 3 tabs to 2 tabs (Channels | Events)

 Toggle + Gear Icon Pattern (like Payment Methods):
- Toggle switch to enable/disable channel for each event
- Gear icon (⚙️) appears when channel is enabled
- Click gear to edit template for that event/channel combination

 Navigation Updates:
- Back button from Edit Template now navigates to Events tab
- Gear icon navigates with correct recipient type (staff/customer)

 Applied to Both:
- Staff Notifications → Events tab
- Customer Notifications → Events tab

 Benefits:
- Cleaner UI with fewer tabs
- More intuitive workflow (enable → configure)
- Consistent pattern across the app
- Less navigation depth

🎯 Next: Restructure Channel Configuration as separate section
This commit is contained in:
dwindown
2025-11-15 20:43:09 +07:00
parent 4471cd600f
commit a8e8d42619
5 changed files with 87 additions and 39 deletions

View File

@@ -7,7 +7,6 @@ import { __ } from '@/lib/i18n';
import { ChevronLeft } from 'lucide-react';
import CustomerChannels from './Customer/Channels';
import CustomerEvents from './Customer/Events';
import NotificationTemplates from './Templates';
export default function CustomerNotifications() {
const [searchParams] = useSearchParams();
@@ -16,7 +15,7 @@ export default function CustomerNotifications() {
// Check for tab query param
useEffect(() => {
const tabParam = searchParams.get('tab');
if (tabParam && ['channels', 'events', 'templates'].includes(tabParam)) {
if (tabParam && ['channels', 'events'].includes(tabParam)) {
setActiveTab(tabParam);
}
}, [searchParams]);
@@ -35,10 +34,9 @@ export default function CustomerNotifications() {
}
>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="channels">{__('Channels')}</TabsTrigger>
<TabsTrigger value="events">{__('Events')}</TabsTrigger>
<TabsTrigger value="templates">{__('Templates')}</TabsTrigger>
</TabsList>
<TabsContent value="channels" className="space-y-4">
@@ -48,10 +46,6 @@ export default function CustomerNotifications() {
<TabsContent value="events" className="space-y-4">
<CustomerEvents />
</TabsContent>
<TabsContent value="templates" className="space-y-4">
<NotificationTemplates />
</TabsContent>
</Tabs>
</SettingsLayout>
);

View File

@@ -1,11 +1,12 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { SettingsCard } from '../../components/SettingsCard';
import { Switch } from '@/components/ui/switch';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { RefreshCw, Mail, MessageCircle, Send, Bell } from 'lucide-react';
import { RefreshCw, Mail, MessageCircle, Send, Bell, Settings } from 'lucide-react';
import { toast } from 'sonner';
import { __ } from '@/lib/i18n';
@@ -33,6 +34,7 @@ interface NotificationChannel {
}
export default function CustomerEvents() {
const navigate = useNavigate();
const queryClient = useQueryClient();
// Fetch customer events
@@ -90,6 +92,10 @@ export default function CustomerEvents() {
});
};
const openTemplateEditor = (eventId: string, channelId: string) => {
navigate(`/settings/notifications/edit-template?event=${eventId}&channel=${channelId}&recipient=customer`);
};
if (eventsLoading || channelsLoading) {
return (
<div className="flex items-center justify-center p-8">
@@ -147,11 +153,23 @@ export default function CustomerEvents() {
)}
</div>
</div>
<Switch
checked={isEnabled}
onCheckedChange={() => handleToggle(event.id, channel.id, isEnabled, recipient)}
disabled={updateMutation.isPending}
/>
<div className="flex items-center gap-2">
{isEnabled && channel.enabled && (
<Button
variant="ghost"
size="sm"
onClick={() => openTemplateEditor(event.id, channel.id)}
className="h-8 w-8 p-0"
>
<Settings className="h-4 w-4" />
</Button>
)}
<Switch
checked={isEnabled}
onCheckedChange={() => handleToggle(event.id, channel.id, isEnabled, recipient)}
disabled={updateMutation.isPending}
/>
</div>
</div>
);
})}

View File

@@ -423,7 +423,7 @@ export default function EditTemplate() {
// Determine if staff or customer based on event category
const isStaffEvent = template.event_category === 'staff' || eventId?.includes('admin') || eventId?.includes('staff');
const page = isStaffEvent ? 'staff' : 'customer';
navigate(`/settings/notifications/${page}?tab=templates`);
navigate(`/settings/notifications/${page}?tab=events`);
}}
className="gap-2"
title={__('Back')}

View File

@@ -7,7 +7,6 @@ import { __ } from '@/lib/i18n';
import { ChevronLeft } from 'lucide-react';
import StaffChannels from './Staff/Channels';
import StaffEvents from './Staff/Events';
import NotificationTemplates from './Templates';
export default function StaffNotifications() {
const [searchParams] = useSearchParams();
@@ -16,7 +15,7 @@ export default function StaffNotifications() {
// Check for tab query param
useEffect(() => {
const tabParam = searchParams.get('tab');
if (tabParam && ['channels', 'events', 'templates'].includes(tabParam)) {
if (tabParam && ['channels', 'events'].includes(tabParam)) {
setActiveTab(tabParam);
}
}, [searchParams]);
@@ -35,10 +34,9 @@ export default function StaffNotifications() {
}
>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="channels">{__('Channels')}</TabsTrigger>
<TabsTrigger value="events">{__('Events')}</TabsTrigger>
<TabsTrigger value="templates">{__('Templates')}</TabsTrigger>
</TabsList>
<TabsContent value="channels" className="space-y-4">
@@ -48,10 +46,6 @@ export default function StaffNotifications() {
<TabsContent value="events" className="space-y-4">
<StaffEvents />
</TabsContent>
<TabsContent value="templates" className="space-y-4">
<NotificationTemplates />
</TabsContent>
</Tabs>
</SettingsLayout>
);

View File

@@ -1,11 +1,12 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { SettingsCard } from '../../components/SettingsCard';
import { Switch } from '@/components/ui/switch';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { RefreshCw, Mail, MessageCircle, Send, Bell } from 'lucide-react';
import { RefreshCw, Mail, MessageCircle, Send, Bell, Settings } from 'lucide-react';
import { toast } from 'sonner';
import { __ } from '@/lib/i18n';
@@ -33,6 +34,7 @@ interface NotificationChannel {
}
export default function NotificationEvents() {
const navigate = useNavigate();
const queryClient = useQueryClient();
// Fetch staff events
@@ -88,6 +90,10 @@ export default function NotificationEvents() {
});
};
const openTemplateEditor = (eventId: string, channelId: string) => {
navigate(`/settings/notifications/edit-template?event=${eventId}&channel=${channelId}&recipient=staff`);
};
if (eventsLoading || channelsLoading) {
return (
<div className="flex items-center justify-center py-12">
@@ -166,11 +172,23 @@ export default function NotificationEvents() {
)}
</div>
</div>
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
<div className="flex items-center gap-2">
{channelEnabled && channel.enabled && (
<Button
variant="ghost"
size="sm"
onClick={() => openTemplateEditor(event.id, channel.id)}
className="h-8 w-8 p-0"
>
<Settings className="h-4 w-4" />
</Button>
)}
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
</div>
</div>
);
})}
@@ -225,11 +243,23 @@ export default function NotificationEvents() {
)}
</div>
</div>
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
<div className="flex items-center gap-2">
{channelEnabled && channel.enabled && (
<Button
variant="ghost"
size="sm"
onClick={() => openTemplateEditor(event.id, channel.id)}
className="h-8 w-8 p-0"
>
<Settings className="h-4 w-4" />
</Button>
)}
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
</div>
</div>
);
})}
@@ -284,11 +314,23 @@ export default function NotificationEvents() {
)}
</div>
</div>
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
<div className="flex items-center gap-2">
{channelEnabled && channel.enabled && (
<Button
variant="ghost"
size="sm"
onClick={() => openTemplateEditor(event.id, channel.id)}
className="h-8 w-8 p-0"
>
<Settings className="h-4 w-4" />
</Button>
)}
<Switch
checked={channelEnabled}
onCheckedChange={() => toggleChannel(event.id, channel.id, channelEnabled)}
disabled={!channel.enabled || updateMutation.isPending}
/>
</div>
</div>
);
})}