import { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import { Layout } from '@/components/layout/Layout'; import { PrimaryButton, DataTable, Modal, FormField, FormTextArea, FormSelect, StatusBadge, Pagination, type Column, } from '@/components/shared'; import { Edit, RotateCcw, Building, Filter } from 'lucide-react'; import { notificationService } from '@/services/notification-service'; import { moduleService } from '@/services/module-service'; import { showToast } from '@/utils/toast'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const overrideSchema = z.object({ title_template: z.string().min(1, 'Title template is required'), message_template: z.string().min(1, 'Message template is required'), email_subject_template: z.string().optional(), email_body_template: z.string().optional(), is_active: z.boolean() }); type OverrideFormValues = z.infer; const NotificationTemplates = (): ReactElement => { const [templates, setTemplates] = useState([]); const [modules, setModules] = useState([]); const [selectedModule, setSelectedModule] = useState('all'); const [isLoading, setIsLoading] = useState(true); // Pagination const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(10); const [totalItems, setTotalItems] = useState(0); const [totalPages, setTotalPages] = useState(0); // Override Modal const [modalOpen, setModalOpen] = useState(false); const [selectedTemplate, setSelectedTemplate] = useState(null); const { register, handleSubmit, reset, formState: { errors } } = useForm({ resolver: zodResolver(overrideSchema), defaultValues: { title_template: '', message_template: '', email_subject_template: '', email_body_template: '', is_active: true } }); const fetchModules = async () => { try { const res = await moduleService.getMyModules(); if (res.success) { setModules(res.data); } } catch (err) { console.error('Failed to fetch modules:', err); } }; const fetchTemplates = async () => { try { setIsLoading(true); const res = await notificationService.getTemplates({ limit, offset: (currentPage - 1) * limit, module_id: selectedModule === 'all' ? undefined : selectedModule }); if (res.success) { setTemplates(res.data); setTotalItems(res.pagination?.total || res.data.length); setTotalPages(res.pagination?.pages || 1); } } catch (err: any) { showToast.error('Failed to load templates'); } finally { setIsLoading(false); } }; useEffect(() => { fetchModules(); }, []); useEffect(() => { fetchTemplates(); }, [currentPage, limit, selectedModule]); const onOverride = async (data: OverrideFormValues) => { try { await notificationService.overrideTemplate(selectedTemplate.code, data); showToast.success('Template override saved'); setModalOpen(false); reset(); fetchTemplates(); } catch (err: any) { const errorMessage = err.response?.data?.error?.message || err.message || 'Action failed'; showToast.error(errorMessage); } }; const handleReset = async (code: string) => { if (!confirm('Are you sure you want to reset this template to the global default? Your custom changes will be lost.')) return; try { await notificationService.resetTemplate(code); showToast.success('Template reset to default'); fetchTemplates(); } catch (err: any) { showToast.error(err.message || 'Reset failed'); } }; const columns: Column[] = [ { key: 'category', label: 'Category', render: (t) => {t.category_name} }, { key: 'code', label: 'Event', render: (t) => {t.code} }, { key: 'source', label: 'Source', render: (t) => ( {t.tenant_id ? 'Custom Override' : 'System Default'} )}, { key: 'preview', label: 'Title Preview', render: (t) => {t.title_template} }, { key: 'actions', label: 'Actions', align: 'right', render: (t) => (
{t.tenant_id && ( )}
) } ]; return (

Notification Templates

Filter by Module
{ setSelectedModule(val); setCurrentPage(1); }} options={[ { value: 'all', label: 'All Modules' }, ...modules.map(m => ({ value: m.id, label: m.name })) ]} placeholder="Select Module" />
t.code} />
{ setLimit(l); setCurrentPage(1); }} />
setModalOpen(false)} title={`Customize: ${selectedTemplate?.code}`} maxWidth="2xl">

In-App Notification

Email Notification

💡 You can use placeholders like {`{{user_name}}`}, {`{{entity_name}}`}, and {`{{action}}`}.
Save Override
); }; export default NotificationTemplates;