fix: Polish notification UI - mobile, colors, and layout
## ✅ All UI Improvements ### 1. Contextual Header - Added contextual header to Notifications page - Consistent with Payments and Shipping pages - Saves vertical space ### 2. Mobile View Improvements **Channels Page:** - Responsive flex-col on mobile, flex-row on desktop - Full-width buttons on mobile - Better spacing and alignment - Push subscription toggle in bordered container on mobile **Templates Accordion:** - Better mobile layout - Badges wrap properly - Icon and title alignment improved - Responsive padding ### 3. Active State Colors - **Green color for active channels** (consistent with Payments) - `bg-green-500/20 text-green-600` for active - `bg-muted text-muted-foreground` for inactive - Applied to: - Events page channel icons - Channels page channel icons - Active badges ### 4. Badge Layout - Badges moved under title on mobile - Better visual hierarchy - Title → Badges → Description flow - Proper spacing between elements ### 5. Template Variables Card Removed - Variables already in template editor modal - Click-to-insert functionality - No need for separate reference card - Cleaner page layout ### 6. Accordion Polish - Better padding and spacing - Responsive layout - Icon stays visible - Badges wrap on small screens --- **Next: Email toggle and push settings backend** 🎯
This commit is contained in:
@@ -13,6 +13,7 @@ export default function NotificationsSettings() {
|
||||
<SettingsLayout
|
||||
title={__('Notifications')}
|
||||
description={__('Manage notification events, channels, and templates')}
|
||||
action={<div />} // Empty action to trigger contextual header
|
||||
>
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
|
||||
@@ -169,20 +169,27 @@ export default function NotificationChannels() {
|
||||
<SettingsCard title={__('Built-in Channels')} description={__('Channels included with WooNooW')}>
|
||||
<div className="space-y-4">
|
||||
{builtinChannels.map((channel: NotificationChannel) => (
|
||||
<div key={channel.id} className="flex items-center justify-between p-4 rounded-lg border bg-card">
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<div className="p-3 rounded-lg bg-primary/10">{getChannelIcon(channel.id)}</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<div key={channel.id} className="flex flex-col sm:flex-row sm:items-center gap-4 p-4 rounded-lg border bg-card">
|
||||
<div className="flex items-start gap-3 flex-1 min-w-0">
|
||||
<div className={`p-3 rounded-lg shrink-0 ${channel.enabled ? 'bg-green-500/20 text-green-600' : 'bg-muted text-muted-foreground'}`}>
|
||||
{getChannelIcon(channel.id)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h3 className="font-medium">{channel.label}</h3>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2 mb-2">
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{__('Built-in')}
|
||||
</Badge>
|
||||
<Badge variant={channel.enabled ? 'default' : 'secondary'} className="text-xs">
|
||||
<Badge
|
||||
variant={channel.enabled ? 'default' : 'secondary'}
|
||||
className={`text-xs ${channel.enabled ? 'bg-green-500 hover:bg-green-600' : ''}`}
|
||||
>
|
||||
{channel.enabled ? __('Active') : __('Inactive')}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{channel.id === 'email' &&
|
||||
__('Email notifications powered by WooCommerce. Configure templates and SMTP settings.')}
|
||||
{channel.id === 'push' &&
|
||||
@@ -190,7 +197,7 @@ export default function NotificationChannels() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -198,32 +205,31 @@ export default function NotificationChannels() {
|
||||
setSelectedChannel(channel);
|
||||
setConfigOpen(true);
|
||||
}}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
<Settings className="h-4 w-4 mr-2" />
|
||||
{__('Configure')}
|
||||
<Settings className="h-4 w-4 sm:mr-2" />
|
||||
<span className="sm:inline">{__('Configure')}</span>
|
||||
</Button>
|
||||
{channel.id === 'push' && pushSupported && (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{pushSubscribed ? __('Subscribed') : __('Not subscribed')}
|
||||
</span>
|
||||
<Switch
|
||||
checked={pushSubscribed}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
subscribeToPush.mutate();
|
||||
} else {
|
||||
unsubscribeFromPush.mutate();
|
||||
}
|
||||
}}
|
||||
disabled={subscribeToPush.isPending || unsubscribeFromPush.isPending}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between sm:justify-start gap-2 p-2 sm:p-0 rounded-lg sm:rounded-none border sm:border-0">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{pushSubscribed ? __('Subscribed') : __('Not subscribed')}
|
||||
</span>
|
||||
<Switch
|
||||
checked={pushSubscribed}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
subscribeToPush.mutate();
|
||||
} else {
|
||||
unsubscribeFromPush.mutate();
|
||||
}
|
||||
}}
|
||||
disabled={subscribeToPush.isPending || unsubscribeFromPush.isPending}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{channel.id === 'push' && !pushSupported && (
|
||||
<Badge variant="destructive" className="text-xs">
|
||||
<Badge variant="destructive" className="text-xs w-full sm:w-auto justify-center">
|
||||
{__('Not Supported')}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
@@ -150,7 +150,7 @@ export default function NotificationEvents() {
|
||||
className="flex items-center justify-between p-3 rounded-lg border bg-card"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-primary/10' : 'bg-muted'}`}>
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-green-500/20 text-green-600' : 'bg-muted text-muted-foreground'}`}>
|
||||
{getChannelIcon(channel.id)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -212,7 +212,7 @@ export default function NotificationEvents() {
|
||||
className="flex items-center justify-between p-3 rounded-lg border bg-card"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-primary/10' : 'bg-muted'}`}>
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-green-500/20 text-green-600' : 'bg-muted text-muted-foreground'}`}>
|
||||
{getChannelIcon(channel.id)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -274,7 +274,7 @@ export default function NotificationEvents() {
|
||||
className="flex items-center justify-between p-3 rounded-lg border bg-card"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-primary/10' : 'bg-muted'}`}>
|
||||
<div className={`p-2 rounded-lg ${channelEnabled ? 'bg-green-500/20 text-green-600' : 'bg-muted text-muted-foreground'}`}>
|
||||
{getChannelIcon(channel.id)}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -125,20 +125,22 @@ export default function NotificationTemplates() {
|
||||
const customCount = channelTemplates.length;
|
||||
|
||||
return (
|
||||
<AccordionItem key={channel.id} value={channel.id}>
|
||||
<AccordionTrigger className="hover:no-underline">
|
||||
<div className="flex items-center gap-3 flex-1">
|
||||
<div className="p-2 rounded-lg bg-primary/10">{getChannelIcon(channel.id)}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{channel.label} {__('Templates')}</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{allEvents.length} {__('templates')}
|
||||
</Badge>
|
||||
{customCount > 0 && (
|
||||
<Badge variant="default" className="text-xs">
|
||||
{customCount} {__('custom')}
|
||||
<AccordionItem key={channel.id} value={channel.id} className="border-b">
|
||||
<AccordionTrigger className="hover:no-underline py-4">
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="p-2 rounded-lg bg-primary/10 shrink-0">{getChannelIcon(channel.id)}</div>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2 flex-1 min-w-0">
|
||||
<span className="font-medium text-left">{channel.label}</span>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{allEvents.length} {__('templates')}
|
||||
</Badge>
|
||||
)}
|
||||
{customCount > 0 && (
|
||||
<Badge variant="default" className="text-xs">
|
||||
{customCount} {__('custom')}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
@@ -181,47 +183,6 @@ export default function NotificationTemplates() {
|
||||
</Accordion>
|
||||
</SettingsCard>
|
||||
|
||||
|
||||
{/* Template Variables Reference */}
|
||||
<SettingsCard
|
||||
title={__('Template Variables')}
|
||||
description={__('Available variables you can use in your templates')}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="font-medium text-sm mb-2">{__('Order Variables')}</h4>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{order_number}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{order_total}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{order_status}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{order_date}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{payment_method}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{shipping_method}'}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium text-sm mb-2">{__('Customer Variables')}</h4>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{customer_name}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{customer_email}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{customer_phone}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{billing_address}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{shipping_address}'}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium text-sm mb-2">{__('Store Variables')}</h4>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{store_name}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{store_url}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{store_email}'}</code>
|
||||
<code className="px-2 py-1 rounded bg-muted text-xs">{'{store_phone}'}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
{/* Template Editor Dialog */}
|
||||
{selectedEvent && selectedChannel && (
|
||||
<TemplateEditor
|
||||
|
||||
Reference in New Issue
Block a user