"use client"; import { useState } from "react"; import { api, type RouterOutputs } from "~/trpc/react"; import { DashboardPageHeader } from "~/components/layout/page-header"; import { DashboardPage } from "~/components/layout/dashboard-page"; import { PageTabs, PageTabsContent, PageTabsList, PageTabsTrigger, } from "~/components/layout/page-tabs"; import { Button } from "~/components/ui/button"; import { Card, CardContent } from "~/components/ui/card"; import { Badge } from "~/components/ui/badge"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; import { Checkbox } from "~/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "~/components/ui/dialog"; import { Tabs, TabsList, TabsTrigger } from "~/components/ui/tabs"; import { toast } from "sonner"; import { Plus, Pencil, Trash2, FileText, Star } from "lucide-react"; interface TemplateForm { name: string; type: "notes" | "terms"; content: string; isDefault: boolean; } const defaultForm: TemplateForm = { name: "", type: "notes", content: "", isDefault: false, }; type InvoiceTemplate = RouterOutputs["invoiceTemplates"]["getAll"][number]; interface TemplateListProps { items: InvoiceTemplate[]; type: "notes" | "terms"; isLoading: boolean; onCreate: (type: "notes" | "terms") => void; onEdit: (template: InvoiceTemplate) => void; onDelete: (id: string) => void; } function TemplateList({ items, type, isLoading, onCreate, onEdit, onDelete, }: TemplateListProps) { return (
{isLoading ? (
Loading...
) : items.length === 0 ? (
No {type} templates yet.
) : ( items.map((template) => (

{template.name}

{template.isDefault && ( Default )}

{template.content}

)) )}
); } export default function TemplatesPage() { const [open, setOpen] = useState(false); const [editId, setEditId] = useState(null); const [form, setForm] = useState(defaultForm); const [deleteId, setDeleteId] = useState(null); const [tab, setTab] = useState<"notes" | "terms">("notes"); const utils = api.useUtils(); const { data: templates = [], isLoading } = api.invoiceTemplates.getAll.useQuery(); const create = api.invoiceTemplates.create.useMutation({ onSuccess: () => { toast.success("Template created"); void utils.invoiceTemplates.getAll.invalidate(); setOpen(false); setForm(defaultForm); }, onError: (e) => toast.error(e.message), }); const update = api.invoiceTemplates.update.useMutation({ onSuccess: () => { toast.success("Template updated"); void utils.invoiceTemplates.getAll.invalidate(); setOpen(false); setEditId(null); setForm(defaultForm); }, onError: (e) => toast.error(e.message), }); const del = api.invoiceTemplates.delete.useMutation({ onSuccess: () => { toast.success("Template deleted"); void utils.invoiceTemplates.getAll.invalidate(); setDeleteId(null); }, onError: (e) => toast.error(e.message), }); const handleOpen = (type: "notes" | "terms") => { setEditId(null); setForm({ ...defaultForm, type }); setOpen(true); }; const handleEdit = (t: InvoiceTemplate) => { setEditId(t.id); setForm({ name: t.name, type: t.type as "notes" | "terms", content: t.content, isDefault: t.isDefault, }); setOpen(true); }; const handleSubmit = () => { if (!form.name.trim()) { toast.error("Name is required"); return; } if (!form.content.trim()) { toast.error("Content is required"); return; } if (editId) update.mutate({ id: editId, ...form }); else create.mutate(form); }; const notesTemplates = templates.filter((t) => t.type === "notes"); const termsTemplates = templates.filter((t) => t.type === "terms"); return ( setTab(v as "notes" | "terms")}> Notes ( {notesTemplates.length}) Terms ( {termsTemplates.length}) {/* Create/Edit dialog */} {editId ? "Edit Template" : "New Template"}
setForm((p) => ({ ...p, name: e.target.value })) } placeholder="e.g. Standard Payment Terms" />
setForm((p) => ({ ...p, type: v as "notes" | "terms" })) } > Notes Terms