From 7c0605d3795c47a88d2c788c177909a4ff9fea85 Mon Sep 17 00:00:00 2001 From: dwindown Date: Tue, 11 Nov 2025 19:00:52 +0700 Subject: [PATCH] feat: Restructure notifications - Staff and Customer separation (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎯 Phase 1: Backend + Frontend Structure ### Backend Changes **NotificationsController.php:** - ✅ Added `/notifications/staff/events` endpoint - ✅ Added `/notifications/customer/events` endpoint - ✅ Created `get_all_events()` helper method - ✅ Added `recipient_type` field to all events - ✅ Filter events by recipient (staff vs customer) ### Frontend Changes **Main Notifications Page:** - ✅ Restructured to show cards for Staff, Customer, Activity Log - ✅ Entry point with clear separation - ✅ Modern card-based UI **Staff Notifications:** - ✅ Created `/settings/notifications/staff` route - ✅ Moved Channels.tsx → Staff/Channels.tsx - ✅ Moved Events.tsx → Staff/Events.tsx - ✅ Updated Staff/Events to use `/notifications/staff/events` - ✅ Fixed import paths ### Structure ``` Settings → Notifications ├── Staff Notifications (admin alerts) │ ├── Channels (Email, Push) │ ├── Events (Orders, Products, Customers) │ └── Templates └── Customer Notifications (customer emails) ├── Channels (Email, Push, SMS) ├── Events (Orders, Shipping, Account) └── Templates ``` --- **Next:** Customer notifications page + routes --- .../src/routes/Settings/Notifications.tsx | 127 ++++-- .../routes/Settings/Notifications/Staff.tsx | 49 +++ .../Settings/Notifications/Staff/Channels.tsx | 366 ++++++++++++++++++ .../Settings/Notifications/Staff/Events.tsx | 303 +++++++++++++++ includes/Api/NotificationsController.php | 181 +++++++++ 5 files changed, 1001 insertions(+), 25 deletions(-) create mode 100644 admin-spa/src/routes/Settings/Notifications/Staff.tsx create mode 100644 admin-spa/src/routes/Settings/Notifications/Staff/Channels.tsx create mode 100644 admin-spa/src/routes/Settings/Notifications/Staff/Events.tsx diff --git a/admin-spa/src/routes/Settings/Notifications.tsx b/admin-spa/src/routes/Settings/Notifications.tsx index fcf03e9..0189301 100644 --- a/admin-spa/src/routes/Settings/Notifications.tsx +++ b/admin-spa/src/routes/Settings/Notifications.tsx @@ -1,39 +1,116 @@ -import React, { useState } from 'react'; +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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; import { __ } from '@/lib/i18n'; -import NotificationEvents from './Notifications/Events'; -import NotificationChannels from './Notifications/Channels'; -import NotificationTemplates from './Notifications/Templates'; +import { Bell, Users, ChevronRight, Activity } from 'lucide-react'; export default function NotificationsSettings() { - const [activeTab, setActiveTab] = useState('events'); - return ( } // Empty action to trigger contextual header > - - - {__('Events')} - {__('Channels')} - {__('Templates')} - +
+ {/* Staff Notifications */} + + +
+
+ +
+
+ {__('Staff Notifications')} + + {__('Alerts for admins and staff members')} + +
+
+
+ +

+ {__('Get notified about orders, low stock, new customers, and more. Configure email and push notifications for your team.')} +

+
+
+ {__('Orders, Products, Customers')} +
+ + + +
+
+
- - - + {/* Customer Notifications */} + + +
+
+ +
+
+ {__('Customer Notifications')} + + {__('Transactional emails and updates for customers')} + +
+
+
+ +

+ {__('Manage order confirmations, shipping updates, account emails, and marketing messages sent to your customers.')} +

+
+
+ {__('Orders, Shipping, Account')} +
+ + + +
+
+
- - - - - - - - + {/* Activity Log */} + + +
+
+ +
+
+ {__('Activity Log')} + + {__('View notification history and activities')} + +
+
+
+ +

+ {__('Track all notification activities, view delivery status, and monitor system events.')} +

+
+
+ {__('Coming soon')} +
+ +
+
+
+
); } diff --git a/admin-spa/src/routes/Settings/Notifications/Staff.tsx b/admin-spa/src/routes/Settings/Notifications/Staff.tsx new file mode 100644 index 0000000..ce4eec7 --- /dev/null +++ b/admin-spa/src/routes/Settings/Notifications/Staff.tsx @@ -0,0 +1,49 @@ +import React, { useState } 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 { __ } 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 [activeTab, setActiveTab] = useState('channels'); + + return ( + + + + } + > + + + {__('Channels')} + {__('Events')} + {__('Templates')} + + + + + + + + + + + + + + + + ); +} diff --git a/admin-spa/src/routes/Settings/Notifications/Staff/Channels.tsx b/admin-spa/src/routes/Settings/Notifications/Staff/Channels.tsx new file mode 100644 index 0000000..1a494a9 --- /dev/null +++ b/admin-spa/src/routes/Settings/Notifications/Staff/Channels.tsx @@ -0,0 +1,366 @@ +import React, { useState, useEffect } from 'react'; +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 { toast } from 'sonner'; +import { __ } from '@/lib/i18n'; +import ChannelConfig from './ChannelConfig'; + +interface NotificationChannel { + id: string; + label: string; + icon: string; + enabled: boolean; + builtin?: boolean; + addon?: string; +} + +// Helper function to convert VAPID key +function urlBase64ToUint8Array(base64String: string) { + const padding = '='.repeat((4 - (base64String.length % 4)) % 4); + const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/'); + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +} + +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(null); + + // Fetch channels + const { data: channels, isLoading } = useQuery({ + queryKey: ['notification-channels'], + queryFn: () => api.get('/notifications/channels'), + }); + + // Toggle channel mutation + const toggleChannelMutation = useMutation({ + mutationFn: async ({ channelId, enabled }: { channelId: string; enabled: boolean }) => { + const response = await api.post('/notifications/channels/toggle', { channelId, enabled }); + return response; + }, + onSuccess: (data, variables) => { + // Update cache with server response + queryClient.setQueryData(['notification-channels'], (old: any) => { + if (!old) return old; + return old.map((channel: any) => + channel.id === variables.channelId + ? { ...channel, enabled: data.enabled } + : channel + ); + }); + toast.success(__('Channel updated')); + }, + onError: (error: any) => { + // Refetch on error to sync with server + queryClient.invalidateQueries({ queryKey: ['notification-channels'] }); + toast.error(error?.message || __('Failed to update channel')); + }, + }); + + // Check push notification support + useEffect(() => { + if ('Notification' in window && 'serviceWorker' in navigator && 'PushManager' in window) { + setPushSupported(true); + // Check if already subscribed + checkPushSubscription(); + } + }, []); + + const checkPushSubscription = async () => { + try { + const registration = await navigator.serviceWorker.ready; + const subscription = await registration.pushManager.getSubscription(); + setPushSubscribed(!!subscription); + } catch (error) { + console.error('Error checking push subscription:', error); + } + }; + + const subscribeToPush = useMutation({ + mutationFn: async () => { + // Request notification permission + const permission = await Notification.requestPermission(); + if (permission !== 'granted') { + throw new Error('Notification permission denied'); + } + + // Get VAPID public key + const { publicKey } = await api.get('/notifications/push/vapid-key'); + + // Register service worker if not already registered + let registration = await navigator.serviceWorker.getRegistration(); + if (!registration) { + // For now, we'll wait for service worker to be registered elsewhere + // In production, you'd register it here + registration = await navigator.serviceWorker.ready; + } + + // Subscribe to push + const subscription = await registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: urlBase64ToUint8Array(publicKey), + }); + + // Send subscription to server + await api.post('/notifications/push/subscribe', { + subscription: subscription.toJSON(), + }); + + return subscription; + }, + onSuccess: () => { + setPushSubscribed(true); + toast.success(__('Push notifications enabled')); + }, + onError: (error: any) => { + console.error('Push subscription error:', error); + toast.error(error?.message || __('Failed to enable push notifications')); + }, + }); + + const unsubscribeFromPush = useMutation({ + mutationFn: async () => { + const registration = await navigator.serviceWorker.ready; + const subscription = await registration.pushManager.getSubscription(); + if (subscription) { + await subscription.unsubscribe(); + // Notify server + await api.post('/notifications/push/unsubscribe', { + subscriptionId: btoa(JSON.stringify(subscription.toJSON())), + }); + } + }, + onSuccess: () => { + setPushSubscribed(false); + toast.success(__('Push notifications disabled')); + }, + onError: (error: any) => { + toast.error(error?.message || __('Failed to disable push notifications')); + }, + }); + + const getChannelIcon = (channelId: string) => { + switch (channelId) { + case 'email': + return ; + case 'whatsapp': + return ; + case 'telegram': + return ; + default: + return ; + } + }; + + if (isLoading) { + return ( +
+ +
+ ); + } + + const builtinChannels = channels?.filter((c: NotificationChannel) => c.builtin) || []; + const addonChannels = channels?.filter((c: NotificationChannel) => !c.builtin) || []; + + return ( +
+ {/* Info Card */} + +
+

+ {__( + '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.' + )} +

+
+
+ + {/* All Channels */} + +
+ {builtinChannels.map((channel: NotificationChannel) => ( +
+
+
+ {getChannelIcon(channel.id)} +
+
+
+

{channel.label}

+ + {__('Built-in')} + +
+

+ {channel.id === 'email' && + __('Email notifications powered by WooCommerce. Configure templates and SMTP settings.')} + {channel.id === 'push' && + __('Browser push notifications for real-time updates. Perfect for PWA.')} +

+
+
+
+ {/* Channel Enable/Disable Toggle */} +
+ + {channel.enabled ? __('Enabled') : __('Disabled')} + + { + toggleChannelMutation.mutate({ channelId: channel.id, enabled: checked }); + // If enabling push, also subscribe + if (channel.id === 'push' && checked && pushSupported && !pushSubscribed) { + subscribeToPush.mutate(); + } + }} + disabled={toggleChannelMutation.isPending} + /> +
+ + + + {channel.id === 'push' && !pushSupported && ( + + {__('Not Supported')} + + )} +
+
+ ))} +
+
+ + {/* Addon Channels */} + {addonChannels.length > 0 ? ( + +
+ {addonChannels.map((channel: NotificationChannel) => ( +
+
+
{getChannelIcon(channel.id)}
+
+
+

{channel.label}

+ + {__('Addon')} + + + {channel.enabled ? __('Active') : __('Inactive')} + +
+

+ {__('Provided by')} {channel.addon} +

+
+
+ +
+ ))} +
+
+ ) : ( + +
+

+ {__( + 'Install notification addons to send notifications via WhatsApp, Telegram, SMS, and more.' + )} +

+ +
+ {/* Example addon cards */} +
+
+ +

{__('WhatsApp Notifications')}

+
+

+ {__('Send order updates and notifications via WhatsApp Business API')} +

+ +
+ +
+
+ +

{__('Telegram Notifications')}

+
+

+ {__('Get instant notifications in your Telegram channel or group')} +

+ +
+ +
+
+ +

{__('SMS Notifications')}

+
+

+ {__('Send SMS notifications via Twilio, Nexmo, or other providers')} +

+ +
+
+
+
+ )} + + {/* Channel Configuration Dialog */} + {selectedChannel && ( + { + setConfigOpen(false); + setSelectedChannel(null); + }} + channelId={selectedChannel.id} + channelLabel={selectedChannel.label} + /> + )} +
+ ); +} diff --git a/admin-spa/src/routes/Settings/Notifications/Staff/Events.tsx b/admin-spa/src/routes/Settings/Notifications/Staff/Events.tsx new file mode 100644 index 0000000..6f433e1 --- /dev/null +++ b/admin-spa/src/routes/Settings/Notifications/Staff/Events.tsx @@ -0,0 +1,303 @@ +import React from 'react'; +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 { toast } from 'sonner'; +import { __ } from '@/lib/i18n'; + +interface NotificationEvent { + id: string; + label: string; + description: string; + category: string; + enabled: boolean; + channels: { + [channelId: string]: { + enabled: boolean; + recipient: 'admin' | 'customer' | 'both'; + }; + }; +} + +interface NotificationChannel { + id: string; + label: string; + icon: string; + enabled: boolean; + builtin?: boolean; + addon?: string; +} + +export default function NotificationEvents() { + const queryClient = useQueryClient(); + + // Fetch staff events + const { data: eventsData, isLoading: eventsLoading } = useQuery({ + queryKey: ['notification-staff-events'], + queryFn: () => api.get('/notifications/staff/events'), + }); + + // Fetch channels + const { data: channels, isLoading: channelsLoading } = useQuery({ + queryKey: ['notification-channels'], + queryFn: () => api.get('/notifications/channels'), + }); + + // Update event mutation + const updateMutation = useMutation({ + mutationFn: async ({ eventId, channelId, enabled, recipient }: any) => { + return api.post('/notifications/events/update', { + eventId, + channelId, + enabled, + recipient, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notification-staff-events'] }); + toast.success(__('Event settings updated')); + }, + onError: (error: any) => { + toast.error(error?.message || __('Failed to update event')); + }, + }); + + const getChannelIcon = (channelId: string) => { + switch (channelId) { + case 'email': + return ; + case 'whatsapp': + return ; + case 'telegram': + return ; + default: + return ; + } + }; + + const toggleChannel = (eventId: string, channelId: string, currentlyEnabled: boolean) => { + updateMutation.mutate({ + eventId, + channelId, + enabled: !currentlyEnabled, + recipient: 'admin', // Default recipient + }); + }; + + if (eventsLoading || channelsLoading) { + return ( +
+ +
+ ); + } + + const orderEvents = eventsData?.orders || []; + const productEvents = eventsData?.products || []; + const customerEvents = eventsData?.customers || []; + + return ( +
+ {/* Info Card */} + +
+

+ {__( + 'Choose which notification channels (Email, WhatsApp, Telegram, etc.) should be used for each event. Enable multiple channels to send notifications through different mediums.' + )} +

+
+

+ 💡 {__('Tip: Email is always available. Install addons to enable WhatsApp, Telegram, SMS, and other channels.')} +

+
+
+
+ + {/* Order Events */} + {orderEvents.length > 0 && ( + +
+ {orderEvents.map((event: NotificationEvent) => ( +
+
+

{event.label}

+

{event.description}

+
+ + {/* Channel Selection */} +
+ {channels?.map((channel: NotificationChannel) => { + const channelEnabled = event.channels?.[channel.id]?.enabled || false; + const recipient = event.channels?.[channel.id]?.recipient || 'admin'; + + return ( +
+
+
+ {getChannelIcon(channel.id)} +
+
+
+ {channel.label} + {channel.builtin && ( + + {__('Built-in')} + + )} +
+ {channelEnabled && ( + + {__('Send to')}: {recipient === 'admin' ? __('Admin') : recipient === 'customer' ? __('Customer') : __('Both')} + + )} +
+
+ toggleChannel(event.id, channel.id, channelEnabled)} + disabled={!channel.enabled || updateMutation.isPending} + /> +
+ ); + })} +
+
+ ))} +
+
+ )} + + {/* Product Events */} + {productEvents.length > 0 && ( + +
+ {productEvents.map((event: NotificationEvent) => ( +
+
+

{event.label}

+

{event.description}

+
+ +
+ {channels?.map((channel: NotificationChannel) => { + const channelEnabled = event.channels?.[channel.id]?.enabled || false; + const recipient = event.channels?.[channel.id]?.recipient || 'admin'; + + return ( +
+
+
+ {getChannelIcon(channel.id)} +
+
+
+ {channel.label} + {channel.builtin && ( + + {__('Built-in')} + + )} +
+ {channelEnabled && ( + + {__('Send to')}: {recipient === 'admin' ? __('Admin') : recipient === 'customer' ? __('Customer') : __('Both')} + + )} +
+
+ toggleChannel(event.id, channel.id, channelEnabled)} + disabled={!channel.enabled || updateMutation.isPending} + /> +
+ ); + })} +
+
+ ))} +
+
+ )} + + {/* Customer Events */} + {customerEvents.length > 0 && ( + +
+ {customerEvents.map((event: NotificationEvent) => ( +
+
+

{event.label}

+

{event.description}

+
+ +
+ {channels?.map((channel: NotificationChannel) => { + const channelEnabled = event.channels?.[channel.id]?.enabled || false; + const recipient = event.channels?.[channel.id]?.recipient || 'admin'; + + return ( +
+
+
+ {getChannelIcon(channel.id)} +
+
+
+ {channel.label} + {channel.builtin && ( + + {__('Built-in')} + + )} +
+ {channelEnabled && ( + + {__('Send to')}: {recipient === 'admin' ? __('Admin') : recipient === 'customer' ? __('Customer') : __('Both')} + + )} +
+
+ toggleChannel(event.id, channel.id, channelEnabled)} + disabled={!channel.enabled || updateMutation.isPending} + /> +
+ ); + })} +
+
+ ))} +
+
+ )} +
+ ); +} diff --git a/includes/Api/NotificationsController.php b/includes/Api/NotificationsController.php index 350e3d2..e3ce415 100644 --- a/includes/Api/NotificationsController.php +++ b/includes/Api/NotificationsController.php @@ -147,6 +147,24 @@ class NotificationsController { 'permission_callback' => [$this, 'check_permission'], ], ]); + + // GET /woonoow/v1/notifications/staff/events + register_rest_route($this->namespace, '/' . $this->rest_base . '/staff/events', [ + [ + 'methods' => 'GET', + 'callback' => [$this, 'get_staff_events'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // GET /woonoow/v1/notifications/customer/events + register_rest_route($this->namespace, '/' . $this->rest_base . '/customer/events', [ + [ + 'methods' => 'GET', + 'callback' => [$this, 'get_customer_events'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); } /** @@ -290,6 +308,169 @@ class NotificationsController { return new WP_REST_Response($events, 200); } + /** + * Get staff notification events (admin/staff recipient) + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response + */ + public function get_staff_events(WP_REST_Request $request) { + $all_events = $this->get_all_events(); + + // Filter events where default recipient is 'admin' or 'staff' + $staff_events = []; + foreach ($all_events as $category => $events) { + $filtered = array_filter($events, function($event) { + $first_channel = reset($event['channels']); + return in_array($first_channel['recipient'] ?? 'admin', ['admin', 'staff']); + }); + + if (!empty($filtered)) { + $staff_events[$category] = array_values($filtered); + } + } + + return new WP_REST_Response($staff_events, 200); + } + + /** + * Get customer notification events (customer recipient) + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response + */ + public function get_customer_events(WP_REST_Request $request) { + $all_events = $this->get_all_events(); + + // Filter events where default recipient is 'customer' + $customer_events = []; + foreach ($all_events as $category => $events) { + $filtered = array_filter($events, function($event) { + $first_channel = reset($event['channels']); + return ($first_channel['recipient'] ?? 'admin') === 'customer'; + }); + + if (!empty($filtered)) { + $customer_events[$category] = array_values($filtered); + } + } + + return new WP_REST_Response($customer_events, 200); + } + + /** + * Get all events (internal helper) + * + * @return array + */ + private function get_all_events() { + // Get saved settings + $settings = get_option('woonoow_notification_settings', []); + + // Define all events + $events = [ + 'orders' => [ + [ + 'id' => 'order_placed', + 'label' => __('Order Placed', 'woonoow'), + 'description' => __('When a new order is placed', 'woonoow'), + 'category' => 'orders', + 'wc_email' => 'new_order', + 'enabled' => true, + 'recipient_type' => 'staff', + 'channels' => $settings['order_placed']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'admin'], 'push' => ['enabled' => false, 'recipient' => 'admin']], + ], + [ + 'id' => 'order_processing', + 'label' => __('Order Processing', 'woonoow'), + 'description' => __('When order status changes to processing', 'woonoow'), + 'category' => 'orders', + 'wc_email' => 'customer_processing_order', + 'enabled' => true, + 'recipient_type' => 'customer', + 'channels' => $settings['order_processing']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'customer'], 'push' => ['enabled' => false, 'recipient' => 'customer']], + ], + [ + 'id' => 'order_completed', + 'label' => __('Order Completed', 'woonoow'), + 'description' => __('When order is marked as completed', 'woonoow'), + 'category' => 'orders', + 'wc_email' => 'customer_completed_order', + 'enabled' => true, + 'recipient_type' => 'customer', + 'channels' => $settings['order_completed']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'customer'], 'push' => ['enabled' => false, 'recipient' => 'customer']], + ], + [ + 'id' => 'order_cancelled', + 'label' => __('Order Cancelled', 'woonoow'), + 'description' => __('When order is cancelled', 'woonoow'), + 'category' => 'orders', + 'wc_email' => 'cancelled_order', + 'enabled' => true, + 'recipient_type' => 'staff', + 'channels' => $settings['order_cancelled']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'admin'], 'push' => ['enabled' => false, 'recipient' => 'admin']], + ], + [ + 'id' => 'order_refunded', + 'label' => __('Order Refunded', 'woonoow'), + 'description' => __('When order is refunded', 'woonoow'), + 'category' => 'orders', + 'wc_email' => 'customer_refunded_order', + 'enabled' => true, + 'recipient_type' => 'customer', + 'channels' => $settings['order_refunded']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'customer'], 'push' => ['enabled' => false, 'recipient' => 'customer']], + ], + ], + 'products' => [ + [ + 'id' => 'low_stock', + 'label' => __('Low Stock Alert', 'woonoow'), + 'description' => __('When product stock is low', 'woonoow'), + 'category' => 'products', + 'wc_email' => 'low_stock', + 'enabled' => true, + 'recipient_type' => 'staff', + 'channels' => $settings['low_stock']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'admin'], 'push' => ['enabled' => false, 'recipient' => 'admin']], + ], + [ + 'id' => 'out_of_stock', + 'label' => __('Out of Stock Alert', 'woonoow'), + 'description' => __('When product is out of stock', 'woonoow'), + 'category' => 'products', + 'wc_email' => 'no_stock', + 'enabled' => true, + 'recipient_type' => 'staff', + 'channels' => $settings['out_of_stock']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'admin'], 'push' => ['enabled' => false, 'recipient' => 'admin']], + ], + ], + 'customers' => [ + [ + 'id' => 'new_customer', + 'label' => __('New Customer', 'woonoow'), + 'description' => __('When a new customer registers', 'woonoow'), + 'category' => 'customers', + 'wc_email' => 'customer_new_account', + 'enabled' => true, + 'recipient_type' => 'customer', + 'channels' => $settings['new_customer']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'customer'], 'push' => ['enabled' => false, 'recipient' => 'customer']], + ], + [ + 'id' => 'customer_note', + 'label' => __('Customer Note Added', 'woonoow'), + 'description' => __('When a note is added to customer order', 'woonoow'), + 'category' => 'customers', + 'wc_email' => 'customer_note', + 'enabled' => true, + 'recipient_type' => 'customer', + 'channels' => $settings['customer_note']['channels'] ?? ['email' => ['enabled' => false, 'recipient' => 'customer'], 'push' => ['enabled' => false, 'recipient' => 'customer']], + ], + ], + ]; + + // Allow addons to add custom events + return apply_filters('woonoow_notification_events', $events); + } + /** * Update event settings *