Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-17 01:44:04 +00:00
co-authored by renee-png
parent ff56718b31
commit 2236a45d91
2 changed files with 221 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
import {
AlignmentType,
Document,
HeightRule,
Packer,
PageOrientation,
Paragraph,
Table,
TableCell,
TableRow,
TextRun,
WidthType,
BorderStyle,
} from "docx";
import { saveAs } from "file-saver";
export interface PleadingInput {
courtType: "CIRCUIT" | "COUNTY";
circuit: string; // e.g. "ELEVENTH"
county: string; // e.g. "Miami-Dade"
plaintiffs: string; // multi-line
defendants: string; // multi-line
caseNumber: string;
title?: string;
body?: string;
}
const FONT = "Bookman Old Style";
const SIZE = 24; // 12pt (docx uses half-points)
const noBorder = {
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
insideHorizontal: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
insideVertical: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
};
const verticalLine = {
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
right: { style: BorderStyle.SINGLE, size: 8, color: "000000" },
};
function run(text: string, opts: { bold?: boolean } = {}) {
return new TextRun({ text, bold: opts.bold, font: FONT, size: SIZE });
}
function p(text: string, opts: { bold?: boolean; align?: (typeof AlignmentType)[keyof typeof AlignmentType] } = {}) {
return new Paragraph({
alignment: opts.align,
children: [run(text, { bold: opts.bold })],
});
}
function emptyP() {
return new Paragraph({ children: [run("")] });
}
export function buildPleadingDoc(input: PleadingInput): Document {
const headerLine1 = `IN THE ${input.courtType} COURT OF THE ${input.circuit} JUDICIAL CIRCUIT,`;
const headerLine2 = `IN AND FOR ${input.county.toUpperCase()} COUNTY, FLORIDA`;
const plaintiffLines = (input.plaintiffs || "").split("\n").filter((l) => l.trim().length > 0);
const defendantLines = (input.defendants || "").split("\n").filter((l) => l.trim().length > 0);
// Left side of caption
const leftCells: Paragraph[] = [];
plaintiffLines.forEach((l) => leftCells.push(p(l)));
if (plaintiffLines.length === 0) leftCells.push(p(""));
leftCells.push(emptyP());
leftCells.push(p("Plaintiff(s),"));
leftCells.push(emptyP());
leftCells.push(p("v."));
leftCells.push(emptyP());
defendantLines.forEach((l) => leftCells.push(p(l)));
if (defendantLines.length === 0) leftCells.push(p(""));
leftCells.push(emptyP());
leftCells.push(p("Defendant(s)."));
const rightCells: Paragraph[] = [
p(`CASE NO.: ${input.caseNumber || ""}`, { bold: true }),
];
const captionTable = new Table({
width: { size: 9360, type: WidthType.DXA },
columnWidths: [5400, 3960],
rows: [
new TableRow({
height: { value: 400, rule: HeightRule.AUTO },
children: [
new TableCell({
width: { size: 5400, type: WidthType.DXA },
borders: verticalLine,
margins: { top: 80, bottom: 80, left: 0, right: 200 },
children: leftCells,
}),
new TableCell({
width: { size: 3960, type: WidthType.DXA },
borders: noBorder,
margins: { top: 80, bottom: 80, left: 200, right: 0 },
children: rightCells,
}),
],
}),
],
});
const bodyParagraphs: Paragraph[] = [];
if (input.title) {
bodyParagraphs.push(emptyP());
bodyParagraphs.push(p(input.title.toUpperCase(), { bold: true, align: AlignmentType.CENTER }));
bodyParagraphs.push(emptyP());
}
if (input.body) {
input.body.split("\n").forEach((line) => bodyParagraphs.push(p(line)));
}
return new Document({
styles: {
default: { document: { run: { font: FONT, size: SIZE } } },
},
sections: [
{
properties: {
page: {
size: { width: 12240, height: 15840, orientation: PageOrientation.PORTRAIT },
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
children: [
p(headerLine1, { bold: true, align: AlignmentType.CENTER }),
p(headerLine2, { bold: true, align: AlignmentType.CENTER }),
emptyP(),
captionTable,
...bodyParagraphs,
],
},
],
});
}
export async function downloadPleading(input: PleadingInput, filename: string) {
const doc = buildPleadingDoc(input);
const blob = await Packer.toBlob(doc);
saveAs(blob, filename.endsWith(".docx") ? filename : `${filename}.docx`);
}
// ---------- Generic template (custom fields) ----------
export interface GenericDocInput {
title?: string;
body: string; // Already merged (fields substituted)
fontFamily?: string;
fontSizePt?: number;
}
export async function downloadGeneric(input: GenericDocInput, filename: string) {
const font = input.fontFamily || "Bookman Old Style";
const size = (input.fontSizePt || 12) * 2;
const mkP = (text: string, bold = false, center = false) =>
new Paragraph({
alignment: center ? AlignmentType.CENTER : undefined,
children: [new TextRun({ text, bold, font, size })],
});
const children: Paragraph[] = [];
if (input.title) {
children.push(mkP(input.title, true, true));
children.push(new Paragraph({ children: [new TextRun({ text: "", font, size })] }));
}
input.body.split("\n").forEach((line) => children.push(mkP(line)));
const doc = new Document({
styles: { default: { document: { run: { font, size } } } },
sections: [
{
properties: {
page: {
size: { width: 12240, height: 15840 },
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
children,
},
],
});
const blob = await Packer.toBlob(doc);
saveAs(blob, filename.endsWith(".docx") ? filename : `${filename}.docx`);
}
+27
View File
@@ -0,0 +1,27 @@
// Florida judicial circuits (1st–20th) and the counties they cover.
export const FL_CIRCUITS: { value: string; label: string; counties: string[] }[] = [
{ value: "FIRST", label: "First", counties: ["Escambia", "Okaloosa", "Santa Rosa", "Walton"] },
{ value: "SECOND", label: "Second", counties: ["Franklin", "Gadsden", "Jefferson", "Leon", "Liberty", "Wakulla"] },
{ value: "THIRD", label: "Third", counties: ["Columbia", "Dixie", "Hamilton", "Lafayette", "Madison", "Suwannee", "Taylor"] },
{ value: "FOURTH", label: "Fourth", counties: ["Clay", "Duval", "Nassau"] },
{ value: "FIFTH", label: "Fifth", counties: ["Citrus", "Hernando", "Lake", "Marion", "Sumter"] },
{ value: "SIXTH", label: "Sixth", counties: ["Pasco", "Pinellas"] },
{ value: "SEVENTH", label: "Seventh", counties: ["Flagler", "Putnam", "St. Johns", "Volusia"] },
{ value: "EIGHTH", label: "Eighth", counties: ["Alachua", "Baker", "Bradford", "Gilchrist", "Levy", "Union"] },
{ value: "NINTH", label: "Ninth", counties: ["Orange", "Osceola"] },
{ value: "TENTH", label: "Tenth", counties: ["Hardee", "Highlands", "Polk"] },
{ value: "ELEVENTH", label: "Eleventh", counties: ["Miami-Dade"] },
{ value: "TWELFTH", label: "Twelfth", counties: ["DeSoto", "Manatee", "Sarasota"] },
{ value: "THIRTEENTH", label: "Thirteenth", counties: ["Hillsborough"] },
{ value: "FOURTEENTH", label: "Fourteenth", counties: ["Bay", "Calhoun", "Gulf", "Holmes", "Jackson", "Washington"] },
{ value: "FIFTEENTH", label: "Fifteenth", counties: ["Palm Beach"] },
{ value: "SIXTEENTH", label: "Sixteenth", counties: ["Monroe"] },
{ value: "SEVENTEENTH", label: "Seventeenth", counties: ["Broward"] },
{ value: "EIGHTEENTH", label: "Eighteenth", counties: ["Brevard", "Seminole"] },
{ value: "NINETEENTH", label: "Nineteenth", counties: ["Indian River", "Martin", "Okeechobee", "St. Lucie"] },
{ value: "TWENTIETH", label: "Twentieth", counties: ["Charlotte", "Collier", "Glades", "Hendry", "Lee"] },
];
export const FL_COUNTIES: string[] = Array.from(
new Set(FL_CIRCUITS.flatMap((c) => c.counties)),
).sort();