mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 23:02:07 +00:00
Some checks are pending
* feat(locations): derive site areas from state * chore(locations): keep ticket keys out of source comments * feat(locations): manage ordered site contacts --------- Co-authored-by: Codex Review Integration <codex-review@local.invalid>
228 lines
7.4 KiB
TypeScript
228 lines
7.4 KiB
TypeScript
import type {
|
|
Location,
|
|
LocationContact,
|
|
LocationListItem,
|
|
LocationSite,
|
|
} from "@/domain/locations/types/location";
|
|
|
|
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 readId(record: Record<string, unknown>): string | number {
|
|
const value = record.id ?? record.Id;
|
|
if (typeof value === "string" || typeof value === "number") {
|
|
return value;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function readOptionalId(record: Record<string, unknown>): string | number {
|
|
const value = record.accountId ?? record.AccountId;
|
|
if (typeof value === "string" || typeof value === "number") {
|
|
return value;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
export function formatLocationSiteLabel(
|
|
site: Pick<LocationSite, "code" | "city" | "state">,
|
|
): string {
|
|
const place = [site.city, site.state].filter(Boolean).join(", ");
|
|
if (site.code && place) {
|
|
return `${site.code} · ${place}`;
|
|
}
|
|
return site.code || place || "";
|
|
}
|
|
|
|
/** Table cell label: `BK5 . Dallas, TX` (period separator). */
|
|
export function formatSiteTableLabel(params: {
|
|
code?: string;
|
|
city?: string;
|
|
state?: string;
|
|
fallbackLocation?: string;
|
|
}): string {
|
|
const code = params.code?.trim() ?? "";
|
|
const place =
|
|
[params.city, params.state].filter(Boolean).join(", ") ||
|
|
(params.fallbackLocation?.trim() ?? "");
|
|
if (code && place) {
|
|
return `${code} . ${place}`;
|
|
}
|
|
return code || place || "";
|
|
}
|
|
|
|
export function formatSitePlace(city?: string, state?: string, fallbackLocation = ""): string {
|
|
return [city, state].filter(Boolean).join(", ") || fallbackLocation.trim() || "";
|
|
}
|
|
|
|
/** Wizard preview: `3811 Distribution Dr, Dallas, TX 70068` */
|
|
export function formatLocationAddressPreview(
|
|
location: Pick<Location, "address" | "city" | "state" | "zipCode">,
|
|
): string {
|
|
const street = location.address?.trim() ?? "";
|
|
const city = location.city?.trim() ?? "";
|
|
const stateZip = [location.state?.trim(), location.zipCode?.trim()].filter(Boolean).join(" ");
|
|
const cityStateZip = [city, stateZip].filter(Boolean).join(", ");
|
|
|
|
if (street && cityStateZip) {
|
|
return `${street}, ${cityStateZip}`;
|
|
}
|
|
return street || cityStateZip || "";
|
|
}
|
|
|
|
export function mapLocationSite(raw: unknown): LocationSite {
|
|
const item = (typeof raw === "object" && raw !== null ? raw : {}) as Record<string, unknown>;
|
|
|
|
return {
|
|
id: readId(item),
|
|
code: readString(item, "code", "Code", "siteCode", "SiteCode"),
|
|
city: readString(item, "city", "City"),
|
|
state: readString(item, "state", "State"),
|
|
};
|
|
}
|
|
|
|
export function mapLocationSites(rawItems: unknown[]): LocationSite[] {
|
|
return rawItems.map(mapLocationSite);
|
|
}
|
|
|
|
/** Reads the additive contacts array; returns [] when absent or malformed. */
|
|
export function mapLocationContacts(raw: unknown): LocationContact[] {
|
|
if (!Array.isArray(raw)) {
|
|
return [];
|
|
}
|
|
return raw.map((entry) => {
|
|
const item = (typeof entry === "object" && entry !== null ? entry : {}) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
const id = item.id ?? item.Id;
|
|
return {
|
|
...(typeof id === "number" && Number.isInteger(id) && id > 0 ? { id } : {}),
|
|
name: readString(item, "name", "Name", "contact", "Contact"),
|
|
phone: readString(item, "phone", "Phone"),
|
|
};
|
|
});
|
|
}
|
|
|
|
export function mapLocationListItem(raw: unknown): LocationListItem {
|
|
const item = (typeof raw === "object" && raw !== null ? raw : {}) as Record<string, unknown>;
|
|
|
|
const contacts = mapLocationContacts(item.contacts ?? item.Contacts);
|
|
const mainContact = contacts[0];
|
|
|
|
return {
|
|
id: readId(item),
|
|
name: readString(item, "name", "Name"),
|
|
address: readString(item, "address", "Address"),
|
|
city: readString(item, "city", "City"),
|
|
state: readString(item, "state", "State"),
|
|
zipCode: readString(item, "zipCode", "ZipCode", "Zip"),
|
|
phone: mainContact ? mainContact.phone : readString(item, "phone", "Phone"),
|
|
contact: mainContact
|
|
? mainContact.name
|
|
: readString(item, "contact", "Contact", "contactPerson", "ContactPerson"),
|
|
contactEmail: readString(item, "contactEmail", "ContactEmail"),
|
|
status: readString(item, "status", "Status"),
|
|
accountId: readOptionalId(item),
|
|
contacts: contacts.length > 0 ? contacts : undefined,
|
|
};
|
|
}
|
|
|
|
export function mapLocationListItems(rawItems: unknown[]): LocationListItem[] {
|
|
return rawItems.map(mapLocationListItem);
|
|
}
|
|
|
|
export function mapLocation(raw: unknown): Location {
|
|
const item = (typeof raw === "object" && raw !== null ? raw : {}) as Record<string, unknown>;
|
|
|
|
const address =
|
|
readString(item, "address", "Address") ||
|
|
[readString(item, "address1", "Address1"), readString(item, "address2", "Address2")]
|
|
.filter(Boolean)
|
|
.join(", ") ||
|
|
undefined;
|
|
|
|
const contacts = mapLocationContacts(item.contacts ?? item.Contacts);
|
|
const mainContact = contacts[0];
|
|
const legacyPhone = readString(item, "phone", "Phone") || undefined;
|
|
const legacyContact =
|
|
readString(item, "contact", "Contact", "contactPerson", "ContactPerson") || undefined;
|
|
|
|
return {
|
|
id: readId(item) || undefined,
|
|
name: readString(item, "name", "Name") || undefined,
|
|
address: address || undefined,
|
|
city: readString(item, "city", "City") || undefined,
|
|
state: readString(item, "state", "State") || undefined,
|
|
zipCode: readString(item, "zipCode", "ZipCode", "Zip") || undefined,
|
|
phone: mainContact ? mainContact.phone : legacyPhone,
|
|
contact: mainContact ? mainContact.name : legacyContact,
|
|
contactEmail: readString(item, "contactEmail", "ContactEmail") || undefined,
|
|
status: readString(item, "status", "Status") || undefined,
|
|
accountId: readOptionalId(item) || undefined,
|
|
contacts: contacts.length > 0 ? contacts : undefined,
|
|
};
|
|
}
|
|
|
|
/** Request payload contacts: trimmed, in displayed order. */
|
|
export function mapContactsToBackend(raw: unknown): LocationContact[] {
|
|
if (!Array.isArray(raw)) {
|
|
return [];
|
|
}
|
|
return raw
|
|
.map((entry) => {
|
|
const item = (typeof entry === "object" && entry !== null ? entry : {}) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
return {
|
|
...(typeof item.id === "number" && Number.isInteger(item.id) && item.id > 0
|
|
? { id: item.id }
|
|
: {}),
|
|
name: readString(item, "name", "Name", "contact", "Contact").trim(),
|
|
phone: readString(item, "phone", "Phone").trim(),
|
|
};
|
|
})
|
|
.filter((contact) => contact.name !== "" || contact.phone !== "");
|
|
}
|
|
|
|
export function mapLocationToBackend(location: unknown): Record<string, unknown> {
|
|
const item = (typeof location === "object" && location !== null ? location : {}) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
|
|
const contacts = mapContactsToBackend(item.contacts ?? item.Contacts);
|
|
const mainContact = contacts[0];
|
|
|
|
const payload: Record<string, unknown> = {
|
|
name: item.name ?? "",
|
|
address: item.address ?? "",
|
|
city: item.city ?? "",
|
|
state: item.state ?? "",
|
|
zipCode: item.zipCode ?? "",
|
|
phone: mainContact ? mainContact.phone : (item.phone ?? ""),
|
|
contact: mainContact ? mainContact.name : (item.contact ?? ""),
|
|
contactEmail: item.contactEmail ?? "",
|
|
status: item.status ?? "Active",
|
|
accountId: item.accountId ?? "",
|
|
contacts,
|
|
};
|
|
|
|
const id = item.id ?? item.Id;
|
|
if (typeof id === "string" || typeof id === "number") {
|
|
payload.id = id;
|
|
}
|
|
|
|
return payload;
|
|
}
|