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:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user