shoc-frontend-new/src/domain/work-orders/mappers/additional-contacts-mapper.ts
arthur.bassi d93ae683c7 fix(work-orders): restore additionalContacts contract and name+phone extras (SH-218)
Document the backend #66 producer, drop incomplete extras from persist, and show the same required-field errors on POC 2+.
2026-08-18 10:03:14 -03:00

102 lines
2.6 KiB
TypeScript

import type { WorkOrderAdditionalContact } from "@/domain/work-orders/types/work-order-additional-contact";
function asRecord(raw: unknown): Record<string, unknown> {
return typeof raw === "object" && raw !== null ? (raw as Record<string, unknown>) : {};
}
function readString(record: Record<string, unknown>, ...keys: string[]): string {
for (const key of keys) {
const value = record[key];
if (typeof value === "string") {
return value;
}
if (typeof value === "number") {
return String(value);
}
}
return "";
}
function mapContactItem(raw: unknown): WorkOrderAdditionalContact | null {
const item = asRecord(raw);
const name = readString(item, "name", "Name");
const phone = readString(item, "phone", "Phone");
const notes = readString(item, "notes", "Notes");
if (!name.trim() && !phone.trim()) {
return null;
}
const contact: WorkOrderAdditionalContact = {
name,
phone,
};
if (notes.trim()) {
contact.notes = notes;
}
return contact;
}
/** Reads additional POCs from board/detail payloads; missing field yields []. */
export function readAdditionalContacts(
record: Record<string, unknown>,
): WorkOrderAdditionalContact[] {
for (const key of ["additionalContacts", "AdditionalContacts"]) {
const value = record[key];
if (!Array.isArray(value)) {
continue;
}
return value
.map(mapContactItem)
.filter((contact): contact is WorkOrderAdditionalContact => contact !== null);
}
return [];
}
/** True when exactly one of name/phone is filled (not persistable). */
export function isIncompleteAdditionalContact(contact: WorkOrderAdditionalContact): boolean {
const hasName = Boolean(contact.name.trim());
const hasPhone = Boolean(contact.phone.trim());
return hasName !== hasPhone;
}
/** Drops empty and incomplete rows; returns undefined when nothing to persist. */
export function normalizeAdditionalContacts(
contacts: WorkOrderAdditionalContact[],
): WorkOrderAdditionalContact[] | undefined {
const out: WorkOrderAdditionalContact[] = [];
for (const raw of contacts) {
const name = raw.name.trim();
const phone = raw.phone.trim();
const notes = raw.notes?.trim();
if (!name && !phone) {
continue;
}
if (!name || !phone) {
continue;
}
const contact: WorkOrderAdditionalContact = { name, phone };
if (notes) {
contact.notes = notes;
}
out.push(contact);
}
return out.length > 0 ? out : undefined;
}
export function serializeAdditionalContactsForPatch(
contacts: WorkOrderAdditionalContact[],
): string {
return JSON.stringify(normalizeAdditionalContacts(contacts) ?? []);
}