import { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import { Layout } from '@/components/layout/Layout'; import { PrimaryButton, DataTable, Modal, FormField, FormSelect, FormTextArea, Pagination, type Column, } from '@/components/shared'; import { Plus, Search, Filter } from 'lucide-react'; import { notificationService } from '@/services/notification-service'; import { moduleService } from '@/services/module-service'; import { showToast } from '@/utils/toast'; const NotificationTemplateMaster = (): ReactElement => { const [templates, setTemplates] = useState([]); const [modules, setModules] = useState([]); const [selectedModule, setSelectedModule] = useState('all'); const [categories, setCategories] = useState([]); const [codes, setCodes] = useState([]); const [isLoading, setIsLoading] = useState(true); // Pagination & Search const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(10); const [totalItems, setTotalItems] = useState(0); const [totalPages, setTotalPages] = useState(0); const [search, setSearch] = useState(''); // Template Modal const [modalOpen, setModalOpen] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ code: '', name: '', description: '', category: '', title_template: '', message_template: '', email_subject_template: '', email_body_template: '', default_priority: 'normal', channels: ['in_app', 'email'], is_active: true }); const fetchData = async () => { try { setIsLoading(true); const [tRes, cRes, mRes] = await Promise.all([ notificationService.getSuperAdminTemplates({ limit, offset: (currentPage - 1) * limit, search, module_id: selectedModule === 'all' ? undefined : selectedModule }), notificationService.getCategories({ limit: 100 }), // Fetch more for dropdown moduleService.getAll(1, 100) ]); if (tRes.success) { setTemplates(tRes.data); setTotalItems(tRes.pagination?.total || tRes.data.length); setTotalPages(tRes.pagination?.pages || 1); } if (cRes.success) { setCategories(cRes.data); } if (mRes.success) { setModules(mRes.data); } } catch (err: any) { showToast.error(err.message || 'Failed to fetch data'); } finally { setIsLoading(false); } }; useEffect(() => { fetchData(); }, [currentPage, limit, search, selectedModule]); const fetchCodesForCategory = async (categoryCode: string) => { if (!categoryCode) return; try { const res = await notificationService.getCodesByCategory(categoryCode, { limit: 100 }); if (res.success) setCodes(res.data); } catch (e) { console.error('Failed to load codes:', e); } }; const handleCategorySelect = async (categoryCode: string) => { setForm(f => ({ ...f, category: categoryCode, code: '' })); await fetchCodesForCategory(categoryCode); }; const handleSave = async () => { try { // For now we use createTemplate (which can override based on code) await notificationService.createTemplate(form); showToast.success(editingId ? 'Template updated' : 'Template created'); setModalOpen(false); fetchData(); } catch (err: any) { showToast.error(err.message || 'Action failed'); } }; const columns: Column[] = [ { key: 'category', label: 'Category', render: (t) => {t.category_name} }, { key: 'code', label: 'Code', render: (t) => {t.code} }, { key: 'name', label: 'Friendly Name', render: (t) => {t.name} }, { key: 'title', label: 'Preview', render: (t) => {t.title_template} }, { key: 'priority', label: 'Priority', render: (t) => {t.default_priority} }, { key: 'channels', label: 'Channels', render: (t) => (
{t.channels?.map((c: string) => ( {c} ))}
) }, { key: 'actions', label: 'Actions', align: 'right', render: (t) => ( ) } ]; return (
{ setSearch(e.target.value); setCurrentPage(1); }} />
Module:
{ setSelectedModule(val); setCurrentPage(1); }} options={[ { value: 'all', label: 'All Modules' }, ...modules.map(m => ({ value: m.id, label: m.name })) ]} placeholder="Filter by Module" />
{ setEditingId(null); setForm({ code: '', name: '', description: '', category: '', title_template: '', message_template: '', email_subject_template: '', email_body_template: '', default_priority: 'normal', channels: ['in_app', 'email'], is_active: true }); setModalOpen(true); }} className="flex gap-2"> New Template
t.id} />
{ setLimit(l); setCurrentPage(1); }} />
setModalOpen(false)} title={editingId ? "Edit Notification Template" : "Create New Template"} maxWidth="2xl">

Identification

c.code === form.category)?.name || form.category }] : categories.map(c => ({ value: c.code, label: c.name })) } disabled={!!editingId} placeholder="Select Category" /> { const selectedCode = codes.find(c => c.code === val); setForm({ ...form, code: val, name: selectedCode?.name || form.name }); }} options={editingId ? [{ value: form.code, label: `${form.name} (${form.code})` }] : codes.map(c => ({ value: c.code, label: `${c.name} (${c.code})` })) } disabled={!!editingId || !form.category} placeholder="Select Event Code" /> setForm({...form, name: e.target.value})} placeholder="e.g. Project Assigned" /> setForm({...form, description: e.target.value})} rows={2} />

Settings

setForm({...form, default_priority: val})} options={[ { value: 'low', label: 'Low' }, { value: 'normal', label: 'Normal' }, { value: 'high', label: 'High' }, { value: 'urgent', label: 'Urgent' } ]} />

In-App Content

setForm({...form, title_template: e.target.value})} placeholder="e.g. Task Assigned: {{task_name}}" /> setForm({...form, message_template: e.target.value})} placeholder="Use {{var}} for dynamic data" rows={3} />

Email Content

setForm({...form, email_subject_template: e.target.value})} placeholder="Leave blank to use In-App title" /> setForm({...form, email_body_template: e.target.value})} placeholder="Full HTML body template..." rows={6} />
{editingId ? 'Update Template' : 'Create Template'}
); }; export default NotificationTemplateMaster;