import { IconAlertCircle as AlertCircle, IconAlertTriangle as AlertTriangle, IconCircleCheck as CheckCircle2, IconInfoCircle as Info, IconLoader2 as Loader2, IconDots as MoreHorizontal, IconPencil as Pencil, IconPlus as Plus, IconTrash as Trash2, } from '@tabler/icons-react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { PageHeader } from '@/components/PageHeader'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { apiJson } from '@/lib/api'; import { ContentPanel } from '../../components/ContentPanel'; type NotificationType = 'info' | 'warning' | 'error' | 'success'; type NotificationVisibility = 'all' | 'authenticated' | 'public'; type AdminNotification = { id: string; title: string; message?: string; type: NotificationType; visibility: NotificationVisibility; dismissible: boolean; organizationId?: string; startsAt?: string; endsAt?: string; actionLabel?: string; actionUrl?: string; isActive: boolean; createdBy?: string; createdAt: string; updatedAt: string; }; type NotificationFormData = { title: string; message: string; type: NotificationType; visibility: NotificationVisibility; dismissible: boolean; isActive: boolean; actionLabel: string; actionUrl: string; }; const defaultFormData: NotificationFormData = { title: '', message: '', type: 'info', visibility: 'all', dismissible: true, isActive: true, actionLabel: '', actionUrl: '', }; async function fetchAdminNotifications(): Promise { const data = await apiJson<{ notifications: AdminNotification[] }>( '/api/v1/admin/notifications', {}, 'Failed to fetch notifications' ); return data.notifications; } export async function createNotification(data: NotificationFormData): Promise { const result = await apiJson<{ notification: AdminNotification }>( '/api/v1/admin/notifications', { method: 'POST', body: { title: data.title, message: data.message || undefined, type: data.type, visibility: data.visibility, dismissible: data.dismissible, isActive: data.isActive, actionLabel: data.actionLabel || undefined, actionUrl: data.actionUrl || undefined, }, }, 'Failed to create notification' ); return result.notification; } async function updateNotification( id: string, data: Partial ): Promise { const result = await apiJson<{ notification: AdminNotification }>( `/api/v1/admin/notifications/${id}`, { method: 'PATCH', body: data }, 'Failed to update notification' ); return result.notification; } async function deleteNotification(id: string): Promise { await apiJson( `/api/v1/admin/notifications/${id}`, { method: 'DELETE' }, 'Failed to delete notification' ); } function getTypeIcon(type: NotificationType) { switch (type) { case 'error': return ; case 'warning': return ; case 'success': return ; default: return ; } } function getTypeBadgeVariant(type: NotificationType) { switch (type) { case 'error': return 'destructive'; case 'warning': return 'outline'; case 'success': return 'default'; default: return 'secondary'; } } export default function AdminNotifications() { const queryClient = useQueryClient(); const [isCreateOpen, setIsCreateOpen] = useState(false); const [editingNotification, setEditingNotification] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [formData, setFormData] = useState(defaultFormData); const { data: notifications = [], isLoading, error, } = useQuery({ queryKey: ['admin-notifications'], queryFn: fetchAdminNotifications, }); const createMutation = useMutation({ mutationFn: createNotification, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-notifications'] }); queryClient.invalidateQueries({ queryKey: ['notifications'] }); setIsCreateOpen(false); setFormData(defaultFormData); }, }); const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: string; data: Partial }) => updateNotification(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-notifications'] }); queryClient.invalidateQueries({ queryKey: ['notifications'] }); setEditingNotification(null); setFormData(defaultFormData); }, }); const deleteMutation = useMutation({ mutationFn: deleteNotification, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-notifications'] }); queryClient.invalidateQueries({ queryKey: ['notifications'] }); setDeleteConfirm(null); }, }); const handleCreate = () => { createMutation.mutate(formData); }; const handleUpdate = () => { if (!editingNotification) return; updateMutation.mutate({ id: editingNotification.id, data: formData }); }; const handleEdit = (notification: AdminNotification) => { setFormData({ title: notification.title, message: notification.message || '', type: notification.type, visibility: notification.visibility, dismissible: notification.dismissible, isActive: notification.isActive, actionLabel: notification.actionLabel || '', actionUrl: notification.actionUrl || '', }); setEditingNotification(notification); }; const handleToggleActive = (notification: AdminNotification) => { updateMutation.mutate({ id: notification.id, data: { isActive: !notification.isActive }, }); }; const isFormOpen = isCreateOpen || !!editingNotification; const isPending = createMutation.isPending || updateMutation.isPending; return ( <> All Notifications Active notifications are shown to users. Inactive notifications are hidden. {isLoading ? (
) : error ? (
{error instanceof Error ? error.message : 'Failed to load notifications'}
) : notifications.length === 0 ? (
No notifications yet. Create one to get started.
) : (
{notifications.map((notification) => (
{getTypeIcon(notification.type)}

{notification.title}

{notification.type} {notification.visibility !== 'all' && ( {notification.visibility} )} {!notification.isActive && Inactive}
{notification.message && (

{notification.message}

)}

Created {new Date(notification.createdAt).toLocaleDateString()}

handleToggleActive(notification)} disabled={updateMutation.isPending} /> }> handleEdit(notification)}> Edit setDeleteConfirm(notification)} > Delete
))}
)}
{/* Create/Edit Dialog */} { if (!open) { setIsCreateOpen(false); setEditingNotification(null); setFormData(defaultFormData); } }} > {editingNotification ? 'Edit Notification' : 'Create Notification'} {editingNotification ? 'Update the notification details below.' : 'Create a new notification to display to all users.'}
setFormData({ ...formData, title: e.target.value })} disabled={isPending} />