diff --git a/src/components/forms/letter-generator.tsx b/src/components/forms/letter-generator.tsx index 88ffae8..3a9e027 100644 --- a/src/components/forms/letter-generator.tsx +++ b/src/components/forms/letter-generator.tsx @@ -16,19 +16,11 @@ import { type HomeownerLite, type FirmInfo, } from "@/lib/forms-shared"; +import { fillTokens, loadFormTemplate, type FormTemplateConfig } from "@/lib/form-templates"; import { supabase } from "@/integrations/supabase/client"; import { jsPDF } from "jspdf"; import { toast } from "sonner"; -const DEFAULT_BODY = `Dear {{ownerName}}, - -This letter is to inform you that… - -If you have any questions, please contact our office. - -Sincerely, -{{firmName}}`; - async function loadLogoDataUrl(path: string | null | undefined): Promise { if (!path) return null; const { data: signed } = await supabase.storage.from("firm-logos").createSignedUrl(path, 60); @@ -55,8 +47,9 @@ export function LetterGenerator() { const [logoDataUrl, setLogoDataUrl] = useState(null); const [attorneyName, setAttorneyName] = useState(""); const [subject, setSubject] = useState(""); - const [body, setBody] = useState(DEFAULT_BODY); + const [body, setBody] = useState(""); const [date, setDate] = useState(new Date().toISOString().slice(0, 10)); + const [template, setTemplate] = useState(null); useEffect(() => { fetchFirm().then(async (f) => { @@ -64,6 +57,11 @@ export function LetterGenerator() { const url = await loadLogoDataUrl((f as any)?.logo_storage_path); setLogoDataUrl(url); }); + loadFormTemplate("letter").then((t) => { + setTemplate(t); + // Seed the editable body with the template default on first load. + setBody((prev) => prev || t.body || ""); + }); supabase.auth.getUser().then(async ({ data }) => { const uid = data.user?.id; if (!uid) return; @@ -80,12 +78,19 @@ export function LetterGenerator() { }, []); const handleExport = () => { + if (!template) return; const ctx = { client, homeowner, firmName: firm?.company_name ?? "" }; const renderedBody = applyVariables(body, ctx); const renderedSubject = applyVariables(subject, ctx); + + // Use the template's font / size / margin choices for the body. + const bodyFont = (template.font ?? "times") as "helvetica" | "times" | "courier"; + const bodyFontSize = template.fontSizePt ?? 11; + const margin = template.marginPt ?? 60; + const lh = template.lineHeightPt ?? 14; + const doc = new jsPDF({ unit: "pt", format: "letter" }); const pageW = doc.internal.pageSize.getWidth(); - const margin = 60; const maxW = pageW - margin * 2; const centerX = pageW / 2; @@ -103,13 +108,13 @@ export function LetterGenerator() { } } - // Firm name (left) + Attorney name italic (right) on same line + // Firm name (left) + Attorney name italic (right) const firmName = (firm?.company_name ?? "").toUpperCase(); - doc.setFont("times", "bold"); + doc.setFont(bodyFont, "bold"); doc.setFontSize(12); if (firmName) doc.text(firmName, margin, y); if (attorneyName) { - doc.setFont("times", "italic"); + doc.setFont(bodyFont, "italic"); doc.setFontSize(11); doc.text(attorneyName, pageW - margin, y, { align: "right" }); } @@ -126,47 +131,46 @@ export function LetterGenerator() { if (addr) contactParts.push(addr); if (firm?.contact_phone) contactParts.push(firm.contact_phone); if (firm?.contact_email) contactParts.push(firm.contact_email); - doc.setFont("times", "normal"); + doc.setFont(bodyFont, "normal"); doc.setFontSize(10); if (contactParts.length) { doc.text(contactParts.join(" \u00B7 "), centerX, y, { align: "center" }); y += 8; } - // Horizontal rule doc.setLineWidth(0.75); doc.line(margin, y, pageW - margin, y); y += 28; // Centered bold date - doc.setFont("times", "bold"); + doc.setFont(bodyFont, "bold"); doc.setFontSize(11); doc.text(fmtDateLong(date), centerX, y, { align: "center" }); y += 28; // Recipient block - doc.setFont("times", "normal"); - doc.setFontSize(11); + doc.setFont(bodyFont, "normal"); + doc.setFontSize(bodyFontSize); ownerMailingLines(homeowner).forEach((l) => { doc.text(l, margin, y); - y += 13; + y += lh - 1; }); if (!homeowner && client) { doc.text(client.name, margin, y); - y += 13; + y += lh - 1; clientAddressLines(client).forEach((l) => { doc.text(l, margin, y); - y += 13; + y += lh - 1; }); } y += 18; if (renderedSubject) { - doc.setFont("times", "bold"); + doc.setFont(bodyFont, "bold"); const subjLines = doc.splitTextToSize(`Re: ${renderedSubject}`, maxW); doc.text(subjLines, margin, y); - y += subjLines.length * 13 + 14; - doc.setFont("times", "normal"); + y += subjLines.length * lh + 14; + doc.setFont(bodyFont, "normal"); } const bodyLines = doc.splitTextToSize(renderedBody, maxW); @@ -176,7 +180,25 @@ export function LetterGenerator() { y = 60; } doc.text(line, margin, y); - y += 14; + y += lh; + } + + // Optional signature block from the template (admins can edit it). + if (template.signature) { + y += 18; + const sig = fillTokens(template.signature, { + "firm.name": firm?.company_name ?? "", + "owner.name": homeowner ? `${homeowner.first_name} ${homeowner.last_name}`.trim() : "", + "client.name": client?.name ?? "", + }); + sig.split("\n").forEach((line) => { + if (y > 740) { + doc.addPage(); + y = 60; + } + doc.text(line, margin, y); + y += lh; + }); } doc.save(`Letter_${date}.pdf`); @@ -228,11 +250,13 @@ export function LetterGenerator() {

Variables: {"{{ownerName}}"}, {"{{clientName}}"},{" "} {"{{propertyAddress}}"}, {"{{balance}}"},{" "} - {"{{currentDate}}"}, {"{{firmName}}"} + {"{{currentDate}}"}, {"{{firmName}}"}. The default body, + signature, fonts and margins are managed in{" "} + Settings → Form templates.

-