Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-17 03:21:27 +00:00
co-authored by renee-png
parent 0b76a68698
commit 0881a0efe4
2 changed files with 253 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
import { supabase } from "@/integrations/supabase/client";
import { format } from "date-fns";
export interface ClientLite {
id: string;
name: string;
address_line1: string | null;
address_line2: string | null;
city: string | null;
state: string | null;
postal_code: string | null;
primary_contact_email: string | null;
primary_contact_phone: string | null;
annual_interest_rate: number | null;
}
export interface HomeownerLite {
id: string;
client_id: string;
first_name: string;
last_name: string;
unit_number: string | null;
address: string | null;
email: string | null;
phone: string | null;
opening_balance: number;
}
export async function fetchClients(): Promise<ClientLite[]> {
const { data } = await supabase
.from("clients")
.select(
"id,name,address_line1,address_line2,city,state,postal_code,primary_contact_email,primary_contact_phone,annual_interest_rate",
)
.order("name");
return (data ?? []) as ClientLite[];
}
export async function fetchHomeowners(clientId: string): Promise<HomeownerLite[]> {
const { data } = await supabase
.from("homeowners")
.select("id,client_id,first_name,last_name,unit_number,address,email,phone,opening_balance")
.eq("client_id", clientId)
.order("last_name");
return (data ?? []) as HomeownerLite[];
}
export function ownerFullName(h: HomeownerLite | null | undefined): string {
if (!h) return "";
return `${h.first_name} ${h.last_name}`.trim();
}
export function clientAddressLines(c: ClientLite | null | undefined): string[] {
if (!c) return [];
const lines: string[] = [];
if (c.address_line1) lines.push(c.address_line1);
if (c.address_line2) lines.push(c.address_line2);
const cityLine = [c.city, c.state, c.postal_code].filter(Boolean).join(", ");
if (cityLine) lines.push(cityLine);
return lines;
}
export function ownerMailingLines(h: HomeownerLite | null | undefined): string[] {
if (!h) return [];
const lines: string[] = [ownerFullName(h)];
if (h.address) lines.push(h.address);
return lines;
}
export const SYSTEM_VARIABLES = [
{ key: "{{clientName}}", description: "Client / Association name" },
{ key: "{{ownerName}}", description: "Homeowner full name" },
{ key: "{{propertyAddress}}", description: "Homeowner property address" },
{ key: "{{unitNumber}}", description: "Homeowner unit number" },
{ key: "{{accountNumber}}", description: "Account / unit identifier" },
{ key: "{{balance}}", description: "Current balance" },
{ key: "{{currentDate}}", description: "Today's date" },
{ key: "{{firmName}}", description: "Your firm name" },
] as const;
export function applyVariables(
body: string,
ctx: { client?: ClientLite | null; homeowner?: HomeownerLite | null; firmName?: string },
): string {
const today = format(new Date(), "MMMM d, yyyy");
const replacements: Record<string, string> = {
"{{clientName}}": ctx.client?.name ?? "",
"{{ownerName}}": ownerFullName(ctx.homeowner),
"{{propertyAddress}}": ctx.homeowner?.address ?? "",
"{{unitNumber}}": ctx.homeowner?.unit_number ?? "",
"{{accountNumber}}": ctx.homeowner?.unit_number ?? "",
"{{balance}}": (ctx.homeowner?.opening_balance ?? 0).toFixed(2),
"{{currentDate}}": today,
"{{firmName}}": ctx.firmName ?? "",
};
let out = body;
for (const [k, v] of Object.entries(replacements)) {
out = out.split(k).join(v);
}
return out;
}
export function fmtCurrency(n: number | string | null | undefined): string {
const v = typeof n === "string" ? parseFloat(n) : (n ?? 0);
return (Number.isFinite(v) ? v : 0).toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
export function fmtDateLong(d: Date | string | null | undefined): string {
if (!d) return "";
const date = typeof d === "string" ? new Date(d.includes("T") ? d : d + "T12:00:00") : d;
return format(date, "MMMM d, yyyy");
}
export interface FirmInfo {
company_name: string | null;
address_line1: string | null;
address_line2: string | null;
city: string | null;
state: string | null;
postal_code: string | null;
contact_email: string | null;
contact_phone: string | null;
website: string | null;
}
export async function fetchFirm(): Promise<FirmInfo | null> {
const { data } = await supabase.from("firm_settings").select("*").maybeSingle();
return (data as FirmInfo) ?? null;
}
export async function savePdfToDocuments(opts: {
blob: Blob;
caseId: string;
name: string;
folder?: string;
}) {
const { blob, caseId, name, folder = "Forms & Letters" } = opts;
const path = `${caseId}/${Date.now()}-${name}`;
const { error: upErr } = await supabase.storage
.from("case-documents")
.upload(path, blob, { contentType: "application/pdf", upsert: false });
if (upErr) throw upErr;
const { data: u } = await supabase.auth.getUser();
const { error: insErr } = await supabase.from("documents").insert({
case_id: caseId,
folder,
name,
storage_path: path,
mime_type: "application/pdf",
size_bytes: blob.size,
uploaded_by: u.user?.id ?? null,
});
if (insErr) throw insErr;
}