/** * TemplatesPage -- config template list page with tag filtering, * create/edit/delete actions, and push wizard access. */ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useUIStore } from '@/lib/store' import { FileCode, Plus, Pencil, Trash2, Play, Tag, Loader2, } from 'lucide-react' import { templatesApi, type TemplateResponse, } from '@/lib/templatesApi' import { tenantsApi } from '@/lib/api' import { useAuth, canWrite, isSuperAdmin } from '@/lib/auth' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { cn } from '@/lib/utils' import { formatDate } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { toast } from '@/components/ui/toast' import { CardGridSkeleton } from '@/components/ui/page-skeleton' import { EmptyState } from '@/components/ui/empty-state' import { TemplateEditor } from './TemplateEditor' import { TemplatePushWizard } from './TemplatePushWizard' export function TemplatesPage() { const { user } = useAuth() const queryClient = useQueryClient() const isSuper = isSuperAdmin(user) const { selectedTenantId, setSelectedTenantId } = useUIStore() const { data: tenants } = useQuery({ queryKey: ['tenants'], queryFn: () => tenantsApi.list(), enabled: isSuper, }) const tenantId = isSuper ? (selectedTenantId ?? '') : (user?.tenant_id ?? '') const writable = canWrite(user) const [tagFilter, setTagFilter] = useState() const [view, setView] = useState<'list' | 'editor'>('list') const [editingTemplate, setEditingTemplate] = useState(null) const [pushTemplate, setPushTemplate] = useState(null) const [deleteConfirmId, setDeleteConfirmId] = useState(null) // Fetch templates const { data: templates, isLoading } = useQuery({ queryKey: ['templates', tenantId, tagFilter], queryFn: () => templatesApi.list(tenantId, tagFilter), enabled: !!tenantId, }) // Get unique tags from all templates const allTags = Array.from( new Set((templates ?? []).flatMap((t) => t.tags)), ).sort() // Delete mutation const deleteMutation = useMutation({ mutationFn: (templateId: string) => templatesApi.delete(tenantId, templateId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['templates', tenantId] }) toast({ title: 'Template deleted' }) setDeleteConfirmId(null) }, onError: (err) => toast({ title: 'Delete failed', description: String(err), variant: 'destructive' }), }) // Create handler const handleCreate = () => { setEditingTemplate(null) setView('editor') } // Edit handler const handleEdit = async (templateId: string) => { const full = await templatesApi.get(tenantId, templateId) setEditingTemplate(full) setView('editor') } // Push handler const handlePush = async (templateId: string) => { const full = await templatesApi.get(tenantId, templateId) setPushTemplate(full) } // Save handler const handleSave = async (data: Parameters[1]) => { if (editingTemplate) { await templatesApi.update(tenantId, editingTemplate.id, data) toast({ title: 'Template updated' }) } else { await templatesApi.create(tenantId, data) toast({ title: 'Template created' }) } queryClient.invalidateQueries({ queryKey: ['templates', tenantId] }) setView('list') setEditingTemplate(null) } // Editor view if (view === 'editor') { return ( { setView('list') setEditingTemplate(null) }} /> ) } // List view return (
{/* Header */}

Config Templates

{templates && ( ({templates.length}) )}
{isSuper && tenants && tenants.length > 0 && ( )}
{writable && ( )}
{/* Tag filter bar */} {allTags.length > 0 && (
{allTags.map((tag) => ( ))}
)} {/* Template list */}
{isLoading ? ( ) : !templates || templates.length === 0 ? ( ) : (
{templates.map((template) => (
{template.description && (
{template.description}
)}
{template.tags.length > 0 && (
{template.tags.map((tag) => ( {tag} ))}
)} {template.variable_count} variable(s) Updated {formatDate(template.updated_at)}
{writable && (
)}
))}
)}
{/* Push Wizard */} {pushTemplate && ( setPushTemplate(null)} tenantId={tenantId} template={pushTemplate} /> )} {/* Delete Confirmation */} !o && setDeleteConfirmId(null)} > Delete Template

Are you sure you want to delete this template? This action cannot be undone. Existing push jobs will keep their rendered content.

) }