46 lines
1.9 KiB
TypeScript
46 lines
1.9 KiB
TypeScript
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)));
|
|
}
|