feat: Restructure Channel Configuration as separate section

 New Structure:
Notifications
├── Staff Notifications (toggle only)
├── Customer Notifications (toggle only)
├── Channel Configuration (new section)
│   ├── Email Configuration
│   │   ├── Template Settings (colors, logo, branding)
│   │   └── Connection Settings (wp_mail/SMTP)
│   ├── Push Configuration
│   │   ├── Template Settings (icon, badge, sound)
│   │   └── Connection Settings (browser-native/FCM)
│   └── Future: WhatsApp, Telegram, SMS (addons)
└── Activity Log (coming soon)

 Separation of Concerns:
- Staff/Customer pages: "What to send" (enable/disable)
- Channel Config: "How to send" (global settings)

 Changes:
- Created ChannelConfiguration.tsx (main page listing all channels)
- Created EmailConfiguration.tsx (template + connection tabs)
- Created PushConfiguration.tsx (template + connection tabs)
- Updated Staff/Customer Channels tabs to toggle-only
- Removed Configure buttons from Staff/Customer pages
- Added links to Channel Configuration
- Updated main Notifications page with new card
- Added routing for all new pages

 Benefits:
- Clear separation: enable vs configure
- Global settings apply to both staff & customer
- Scalable for addon channels
- No confusion about where to configure
- Consistent with app patterns

🎯 Ready for: WhatsApp, Telegram, SMS addons
This commit is contained in:
dwindown
2025-11-15 21:05:57 +07:00
parent a8e8d42619
commit 778afeef9a
7 changed files with 399 additions and 41 deletions

View File

@@ -203,6 +203,9 @@ import SettingsLocalPickup from '@/routes/Settings/LocalPickup';
import SettingsNotifications from '@/routes/Settings/Notifications';
import StaffNotifications from '@/routes/Settings/Notifications/Staff';
import CustomerNotifications from '@/routes/Settings/Notifications/Customer';
import ChannelConfiguration from '@/routes/Settings/Notifications/ChannelConfiguration';
import EmailConfiguration from '@/routes/Settings/Notifications/EmailConfiguration';
import PushConfiguration from '@/routes/Settings/Notifications/PushConfiguration';
import EmailCustomization from '@/routes/Settings/Notifications/EmailCustomization';
import EditTemplate from '@/routes/Settings/Notifications/EditTemplate';
import SettingsDeveloper from '@/routes/Settings/Developer';
@@ -494,6 +497,9 @@ function AppRoutes() {
<Route path="/settings/notifications" element={<SettingsNotifications />} />
<Route path="/settings/notifications/staff" element={<StaffNotifications />} />
<Route path="/settings/notifications/customer" element={<CustomerNotifications />} />
<Route path="/settings/notifications/channels" element={<ChannelConfiguration />} />
<Route path="/settings/notifications/channels/email" element={<EmailConfiguration />} />
<Route path="/settings/notifications/channels/push" element={<PushConfiguration />} />
<Route path="/settings/notifications/email-customization" element={<EmailCustomization />} />
<Route path="/settings/notifications/edit-template" element={<EditTemplate />} />
<Route path="/settings/brand" element={<SettingsIndex />} />

View File

@@ -4,7 +4,7 @@ import { SettingsLayout } from './components/SettingsLayout';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { __ } from '@/lib/i18n';
import { Bell, Users, ChevronRight, Activity, Palette } from 'lucide-react';
import { Bell, Users, ChevronRight, Activity, Settings } from 'lucide-react';
export default function NotificationsSettings() {
return (
@@ -80,32 +80,32 @@ export default function NotificationsSettings() {
</CardContent>
</Card>
{/* Email Customization */}
{/* Channel Configuration */}
<Card className="hover:shadow-md transition-shadow">
<CardHeader>
<div className="flex items-center gap-3">
<div className="p-2 bg-green-500/10 rounded-lg">
<Palette className="h-6 w-6 text-green-500" />
<Settings className="h-6 w-6 text-green-500" />
</div>
<div>
<CardTitle>{__('Email Customization')}</CardTitle>
<CardTitle>{__('Channel Configuration')}</CardTitle>
<CardDescription>
{__('Customize email appearance and branding')}
{__('Configure notification channels and settings')}
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
{__('Set your brand colors, logo, and email styling. Customize header, footer, and button colors for all email templates.')}
{__('Configure email, push notifications, WhatsApp, Telegram, and other notification channels. Set templates and connection settings.')}
</p>
<div className="flex items-center justify-between pt-2">
<div className="text-sm text-muted-foreground">
{__('Colors, Logo, Styling')}
{__('Email, Push, WhatsApp, Telegram')}
</div>
<Link to="/settings/notifications/email-customization">
<Link to="/settings/notifications/channels">
<Button variant="outline" size="sm">
{__('Customize')}
{__('Configure')}
<ChevronRight className="ml-2 h-4 w-4" />
</Button>
</Link>

View File

@@ -0,0 +1,168 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { SettingsLayout } from '../components/SettingsLayout';
import { SettingsCard } from '../components/SettingsCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ChevronLeft, Mail, Bell, MessageCircle, Send, Settings, ChevronRight } from 'lucide-react';
import { __ } from '@/lib/i18n';
interface Channel {
id: string;
name: string;
description: string;
icon: React.ReactNode;
type: 'builtin' | 'addon';
enabled: boolean;
configPath: string;
}
export default function ChannelConfiguration() {
const channels: Channel[] = [
{
id: 'email',
name: __('Email'),
description: __('Email notifications powered by WordPress. Configure templates and SMTP settings.'),
icon: <Mail className="h-6 w-6" />,
type: 'builtin',
enabled: true,
configPath: '/settings/notifications/channels/email',
},
{
id: 'push',
name: __('Push Notifications'),
description: __('Browser push notifications for real-time updates. Perfect for PWA.'),
icon: <Bell className="h-6 w-6" />,
type: 'builtin',
enabled: true,
configPath: '/settings/notifications/channels/push',
},
{
id: 'whatsapp',
name: __('WhatsApp'),
description: __('Send notifications via WhatsApp Business API. Requires addon installation.'),
icon: <MessageCircle className="h-6 w-6" />,
type: 'addon',
enabled: false,
configPath: '/settings/notifications/channels/whatsapp',
},
{
id: 'telegram',
name: __('Telegram'),
description: __('Send notifications via Telegram Bot API. Requires addon installation.'),
icon: <Send className="h-6 w-6" />,
type: 'addon',
enabled: false,
configPath: '/settings/notifications/channels/telegram',
},
];
const builtinChannels = channels.filter(c => c.type === 'builtin');
const addonChannels = channels.filter(c => c.type === 'addon');
return (
<SettingsLayout
title={__('Channel Configuration')}
description={__('Configure global settings for each notification channel')}
action={
<Link to="/settings/notifications">
<Button variant="ghost" size="sm">
<ChevronLeft className="mr-2 h-4 w-4" />
{__('Back to Notifications')}
</Button>
</Link>
}
>
<div className="space-y-6">
{/* Info Card */}
<SettingsCard
title={__('About Channel Configuration')}
description={__('Global settings that apply to all notifications')}
>
<div className="text-sm space-y-3">
<p className="text-muted-foreground">
{__(
'Each notification channel has its own configuration for templates and connection settings. These settings apply globally to both staff and customer notifications.'
)}
</p>
<div className="bg-muted/50 rounded-lg p-4">
<p className="text-xs text-muted-foreground">
💡 {__('Tip: Configure your channels here, then enable them for specific events in Staff or Customer Notifications.')}
</p>
</div>
</div>
</SettingsCard>
{/* Built-in Channels */}
<SettingsCard
title={__('Built-in Channels')}
description={__('Always available notification channels')}
>
<div className="space-y-3">
{builtinChannels.map((channel) => (
<div
key={channel.id}
className="flex items-center justify-between p-4 rounded-lg border bg-card hover:bg-accent/50 transition-colors"
>
<div className="flex items-center gap-4">
<div className="p-3 rounded-lg bg-primary/10 text-primary">
{channel.icon}
</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-semibold text-base">{channel.name}</h3>
<Badge variant="secondary" className="text-xs">
{__('Built-in')}
</Badge>
</div>
<p className="text-sm text-muted-foreground">{channel.description}</p>
</div>
</div>
<Link to={channel.configPath}>
<Button variant="outline" size="sm" className="gap-2">
<Settings className="h-4 w-4" />
{__('Configure')}
<ChevronRight className="h-4 w-4" />
</Button>
</Link>
</div>
))}
</div>
</SettingsCard>
{/* Addon Channels */}
<SettingsCard
title={__('Addon Channels')}
description={__('Install addons to enable additional notification channels')}
>
<div className="space-y-3">
{addonChannels.map((channel) => (
<div
key={channel.id}
className="flex items-center justify-between p-4 rounded-lg border bg-card opacity-60"
>
<div className="flex items-center gap-4">
<div className="p-3 rounded-lg bg-muted text-muted-foreground">
{channel.icon}
</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-semibold text-base">{channel.name}</h3>
<Badge variant="outline" className="text-xs">
{__('Addon Required')}
</Badge>
</div>
<p className="text-sm text-muted-foreground">{channel.description}</p>
</div>
</div>
<Button variant="outline" size="sm" disabled>
{__('Install Addon')}
</Button>
</div>
))}
</div>
</SettingsCard>
</div>
</SettingsLayout>
);
}

View File

@@ -1,11 +1,12 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { SettingsCard } from '../../components/SettingsCard';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { RefreshCw, Mail, Bell, MessageSquare, Info, MessageCircle, Send, ExternalLink } from 'lucide-react';
import { RefreshCw, Mail, Bell, MessageSquare, Info, MessageCircle, Send, ExternalLink, ArrowRight } from 'lucide-react';
import { __ } from '@/lib/i18n';
interface NotificationChannel {
@@ -58,6 +59,23 @@ export default function CustomerChannels() {
<SettingsCard
title={__('Channels')}
description={__('Enable or disable notification channels for customers')}
>
<div className="bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-4">
<p className="text-sm text-muted-foreground mb-2">
💡 {__('Need to configure channel settings?')}
</p>
<Link to="/settings/notifications/channels">
<Button variant="outline" size="sm" className="gap-2">
{__('Go to Channel Configuration')}
<ArrowRight className="h-4 w-4" />
</Button>
</Link>
</div>
</SettingsCard>
<SettingsCard
title={__('Available Channels')}
description={__('Manage notification delivery channels')}
>
<div className="space-y-4">

View File

@@ -0,0 +1,53 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { SettingsLayout } from '../components/SettingsLayout';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { ChevronLeft } from 'lucide-react';
import { __ } from '@/lib/i18n';
import EmailCustomization from './EmailCustomization';
export default function EmailConfiguration() {
return (
<SettingsLayout
title={__('Email Configuration')}
description={__('Configure email templates and connection settings')}
action={
<Link to="/settings/notifications/channels">
<Button variant="ghost" size="sm">
<ChevronLeft className="mr-2 h-4 w-4" />
{__('Back to Channels')}
</Button>
</Link>
}
>
<Tabs defaultValue="template" className="space-y-6">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="template">{__('Template Settings')}</TabsTrigger>
<TabsTrigger value="connection">{__('Connection Settings')}</TabsTrigger>
</TabsList>
<TabsContent value="template" className="space-y-4">
<EmailCustomization />
</TabsContent>
<TabsContent value="connection" className="space-y-4">
{/* Connection Settings - SMTP Override */}
<div className="rounded-lg border bg-card p-6">
<h3 className="text-lg font-semibold mb-2">{__('Email Delivery')}</h3>
<p className="text-sm text-muted-foreground mb-4">
{__(
'By default, emails are sent using WordPress wp_mail() function. You can override this with custom SMTP settings if needed.'
)}
</p>
<div className="bg-muted/50 rounded-lg p-4">
<p className="text-sm text-muted-foreground">
💡 {__('Tip: For custom SMTP configuration, install a dedicated SMTP plugin like WP Mail SMTP or Easy WP SMTP.')}
</p>
</div>
</div>
</TabsContent>
</Tabs>
</SettingsLayout>
);
}

View File

@@ -0,0 +1,130 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { SettingsLayout } from '../components/SettingsLayout';
import { SettingsCard } from '../components/SettingsCard';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { ChevronLeft } from 'lucide-react';
import { __ } from '@/lib/i18n';
export default function PushConfiguration() {
return (
<SettingsLayout
title={__('Push Notifications Configuration')}
description={__('Configure push notification templates and connection settings')}
action={
<Link to="/settings/notifications/channels">
<Button variant="ghost" size="sm">
<ChevronLeft className="mr-2 h-4 w-4" />
{__('Back to Channels')}
</Button>
</Link>
}
>
<Tabs defaultValue="template" className="space-y-6">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="template">{__('Template Settings')}</TabsTrigger>
<TabsTrigger value="connection">{__('Connection Settings')}</TabsTrigger>
</TabsList>
<TabsContent value="template" className="space-y-4">
<SettingsCard
title={__('Push Notification Template')}
description={__('Customize how push notifications appear')}
>
<div className="space-y-6">
{/* Icon */}
<div className="space-y-2">
<Label htmlFor="push-icon">{__('Notification Icon URL')}</Label>
<Input
id="push-icon"
type="url"
placeholder="https://example.com/icon.png"
/>
<p className="text-xs text-muted-foreground">
{__('Icon displayed in push notifications (recommended: 192x192px PNG)')}
</p>
</div>
{/* Badge */}
<div className="space-y-2">
<Label htmlFor="push-badge">{__('Badge Icon URL')}</Label>
<Input
id="push-badge"
type="url"
placeholder="https://example.com/badge.png"
/>
<p className="text-xs text-muted-foreground">
{__('Small icon shown on notification badge (recommended: 96x96px PNG)')}
</p>
</div>
{/* Sound */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label>{__('Enable Sound')}</Label>
<p className="text-xs text-muted-foreground">
{__('Play default notification sound')}
</p>
</div>
<Switch defaultChecked />
</div>
{/* Vibrate */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label>{__('Enable Vibration')}</Label>
<p className="text-xs text-muted-foreground">
{__('Vibrate device on notification (mobile only)')}
</p>
</div>
<Switch defaultChecked />
</div>
</div>
</SettingsCard>
</TabsContent>
<TabsContent value="connection" className="space-y-4">
<SettingsCard
title={__('Push Notification Service')}
description={__('Configure how push notifications are delivered')}
>
<div className="space-y-4">
<div className="bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<h4 className="font-medium text-sm mb-2">{__('Browser-Native Push (Default)')}</h4>
<p className="text-sm text-muted-foreground">
{__(
'Uses the browser\'s built-in Push API. No external service required. Works great for PWA applications.'
)}
</p>
</div>
<div className="bg-muted/50 rounded-lg p-4">
<p className="text-sm text-muted-foreground mb-3">
💡 {__('Want more features? Install one of these addons:')}
</p>
<ul className="space-y-2 text-sm text-muted-foreground">
<li className="flex items-start gap-2">
<span className="text-primary"></span>
<span>
<strong>{__('Firebase Cloud Messaging (FCM)')}</strong> - {__('Advanced features, analytics, and cross-platform support')}
</span>
</li>
<li className="flex items-start gap-2">
<span className="text-primary"></span>
<span>
<strong>{__('OneSignal')}</strong> - {__('Easy setup, segmentation, and A/B testing')}
</span>
</li>
</ul>
</div>
</div>
</SettingsCard>
</TabsContent>
</Tabs>
</SettingsLayout>
);
}

View File

@@ -1,14 +1,14 @@
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { SettingsCard } from '../../components/SettingsCard';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { RefreshCw, Mail, MessageCircle, Send, Bell, ExternalLink, Settings, Check, X } from 'lucide-react';
import { RefreshCw, Mail, MessageCircle, Send, Bell, ExternalLink, Check, X, ArrowRight } from 'lucide-react';
import { toast } from 'sonner';
import { __ } from '@/lib/i18n';
import ChannelConfig from '../ChannelConfig';
interface NotificationChannel {
id: string;
@@ -35,8 +35,6 @@ export default function NotificationChannels() {
const queryClient = useQueryClient();
const [pushSubscribed, setPushSubscribed] = useState(false);
const [pushSupported, setPushSupported] = useState(false);
const [configOpen, setConfigOpen] = useState(false);
const [selectedChannel, setSelectedChannel] = useState<NotificationChannel | null>(null);
// Fetch channels
const { data: channels, isLoading } = useQuery({
@@ -185,9 +183,20 @@ export default function NotificationChannels() {
<div className="text-sm space-y-3">
<p className="text-muted-foreground">
{__(
'Channels are the different ways notifications can be sent. Email is built-in and always available. Install addons to enable additional channels like WhatsApp, Telegram, SMS, and more.'
'Enable or disable notification channels for staff notifications. Toggle channels on/off to control which delivery methods are available.'
)}
</p>
<div className="bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<p className="text-sm text-muted-foreground mb-2">
💡 {__('Need to configure channel settings?')}
</p>
<Link to="/settings/notifications/channels">
<Button variant="outline" size="sm" className="gap-2">
{__('Go to Channel Configuration')}
<ArrowRight className="h-4 w-4" />
</Button>
</Link>
</div>
</div>
</SettingsCard>
@@ -234,19 +243,6 @@ export default function NotificationChannels() {
/>
</div>
<Button
variant="outline"
size="sm"
onClick={() => {
setSelectedChannel(channel);
setConfigOpen(true);
}}
className="w-full sm:w-auto"
>
<Settings className="h-4 w-4 sm:mr-2" />
<span className="sm:inline">{__('Configure')}</span>
</Button>
{channel.id === 'push' && !pushSupported && (
<Badge variant="destructive" className="text-xs w-full sm:w-auto justify-center">
{__('Not Supported')}
@@ -348,19 +344,6 @@ export default function NotificationChannels() {
</div>
</SettingsCard>
)}
{/* Channel Configuration Dialog */}
{selectedChannel && (
<ChannelConfig
open={configOpen}
onClose={() => {
setConfigOpen(false);
setSelectedChannel(null);
}}
channelId={selectedChannel.id}
channelLabel={selectedChannel.label}
/>
)}
</div>
);
}