This commit is contained in:
2026-07-15 16:42:28 -05:00
commit 201f5c1b95
11 changed files with 592 additions and 0 deletions

45
src/data.ts Normal file
View File

@@ -0,0 +1,45 @@
import type { BuyerDocument, PersonGroup } from "./types.ts";
const text = (value: unknown) => typeof value === "string" ? value.trim() : "";
const normalize = (value: unknown) => text(value).toLocaleLowerCase();
export function validateDocuments(value: unknown): BuyerDocument[] {
if (!Array.isArray(value)) throw new Error("JSON root must be an array.");
return value.map((item, index) => {
if (!item || typeof item !== "object") throw new Error(`Record ${index + 1} is not an object.`);
const doc = item as Partial<BuyerDocument>;
if (!text(doc.file_name) || !text(doc.name_from_filename) || !text(doc._letter)) {
throw new Error(`Record ${index + 1} is missing file_name, name_from_filename, or _letter.`);
}
return doc as BuyerDocument;
});
}
export function groupDocuments(documents: BuyerDocument[]): PersonGroup[] {
const groups = new Map<string, BuyerDocument[]>();
for (const doc of documents) {
const key = doc.name_from_filename.trim();
const bucket = groups.get(key) ?? [];
bucket.push(doc);
groups.set(key, bucket);
}
return [...groups.entries()]
.map(([key, docs]) => {
docs.sort((a, b) => a.file_name.localeCompare(b.file_name));
const preferred = docs.find((d) => text(d.prospective_buyer))?.prospective_buyer;
const searchText = normalize([
key,
preferred,
...docs.flatMap((d) => [d.types_of_business_raw, d.notes_business_raw, d.address]),
].filter(Boolean).join("\n"));
return { key, displayName: text(preferred) || key, documents: docs, searchText };
})
.sort((a, b) => a.key.localeCompare(b.key));
}
export function filterGroups(groups: PersonGroup[], query: string): PersonGroup[] {
const terms = normalize(query).split(/\s+/).filter(Boolean);
if (!terms.length) return groups;
return groups.filter((group) => terms.every((term) => group.searchText.includes(term)));
}