diff --git a/admin-spa/src/routes/Settings/Notifications/Channels.tsx b/admin-spa/src/routes/Settings/Notifications/Channels.tsx index 97a6597..3b23a6e 100644 --- a/admin-spa/src/routes/Settings/Notifications/Channels.tsx +++ b/admin-spa/src/routes/Settings/Notifications/Channels.tsx @@ -1,10 +1,12 @@ -import React from 'react'; -import { useQuery } from '@tanstack/react-query'; +import React, { useState, useEffect } from 'react'; +import { useQuery, useMutation } 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 { RefreshCw, Mail, MessageCircle, Send, Bell, ExternalLink, Settings } from 'lucide-react'; +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'; interface NotificationChannel { @@ -17,12 +19,89 @@ interface NotificationChannel { } export default function NotificationChannels() { + const [pushSubscribed, setPushSubscribed] = useState(false); + const [pushSupported, setPushSupported] = useState(false); + // Fetch channels const { data: channels, isLoading } = useQuery({ queryKey: ['notification-channels'], queryFn: () => api.get('/notifications/channels'), }); + // 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'); + + // Subscribe to push + const registration = await navigator.serviceWorker.ready; + const subscription = await registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: 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) => { + 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': @@ -68,9 +147,9 @@ export default function NotificationChannels() {
{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.')}