fix(work-orders): extract helpers to satisfy governance complexity

This commit is contained in:
Arthur Bassi 2026-09-10 18:15:02 -03:00
parent 116f65ca48
commit 99c758872a
3 changed files with 257 additions and 122 deletions

View file

@ -32,6 +32,151 @@ type SlideOverInfoTabDisplayViewProps = {
pmTypes: string[];
};
type FrozenPoc = ReturnType<typeof effectivePoc>;
function formatScheduleOn(source: WorkOrderTableRow): string {
if (source.scheduleWeekOnly && source.targetWeek) {
return `Week of ${source.targetWeek}`;
}
return source.scheduledOn || "—";
}
function formatApptDisplay(apptTime: string): string {
const appt = parseApptTime(apptTime);
if (appt.start) return formatApptRange(appt.start, appt.end);
return apptTime || "—";
}
function formatPocLine(poc: FrozenPoc): string {
const primary = poc.contacts[0];
const name = primary?.name || "—";
if (!primary?.phone) return name;
return `${name} · ${primary.phone}`;
}
function SlideOverTypeField({ type }: { type: WorkOrderTableRow["type"] }) {
if (!type) {
return <span style={{ color: "var(--color-text-muted)" }}>—</span>;
}
return <WoTableTypeLabel type={type as WOType} />;
}
function SlideOverCompletedSite({
source,
poc,
sites,
}: {
source: WorkOrderTableRow;
poc: FrozenPoc;
sites: LocationOption[];
}) {
const primaryPoc = poc.contacts[0];
return (
<SiteCell
value={source.site}
location={source.location}
locationId={source.locationId}
pocName={primaryPoc?.name ?? ""}
pocPhone={primaryPoc?.phone ?? ""}
pocNotes={poc.notes ?? ""}
additionalContacts={poc.contacts.slice(1).map((contact) => ({
name: contact.name ?? "",
phone: contact.phone ?? "",
}))}
sites={sites}
viewOnly
frozenSite={source.frozenSite}
onSave={() => undefined}
/>
);
}
function SlideOverSiteField({
source,
isCompleted,
poc,
sites,
}: {
source: WorkOrderTableRow;
isCompleted: boolean;
poc: FrozenPoc;
sites: LocationOption[];
}) {
if (!isCompleted) {
return (
<span className="font-mono text-[12.5px] font-bold" style={{ color: "var(--foreground)" }}>
{source.site || "—"}
</span>
);
}
return <SlideOverCompletedSite source={source} poc={poc} sites={sites} />;
}
function SlideOverOpenService({ source }: { source: WorkOrderTableRow }) {
if (!source.pm) {
return <span style={{ color: "var(--color-text-muted)" }}>—</span>;
}
return (
<span className="inline-flex items-center gap-1.5 text-[12.5px]">
<PmTypeIcon pmType={source.pm} size={14} />
{source.pm}
{Boolean(source.avetaRequired) && (
<AvetaBadge size={11} attached={Boolean(source.hasAvetaDocument)} />
)}
</span>
);
}
function SlideOverServiceField({
source,
isCompleted,
pmTypes,
}: {
source: WorkOrderTableRow;
isCompleted: boolean;
pmTypes: string[];
}) {
if (!isCompleted) return <SlideOverOpenService source={source} />;
return (
<PMTypeCell
value={source.pm}
pmTypes={pmTypes}
serviceNotes={source.serviceNotes}
extraServices={source.extraServices}
avetaRequired={source.avetaRequired}
avetaAttached={source.hasAvetaDocument}
woNumber={source.woNumber}
viewOnly
onSave={() => undefined}
/>
);
}
function SlideOverVendorField({
source,
isCompleted,
vendors,
}: {
source: WorkOrderTableRow;
isCompleted: boolean;
vendors: VendorDropdownItem[];
}) {
if (!isCompleted) return <SlideOverInfoTabVendorField source={source} />;
const company = effectiveCompany(source);
return (
<VendorCell
vendorId={source.vendorId}
company={company.name ?? source.company}
tech={company.contactName ?? source.tech}
techPhone={company.phone ?? source.techPhone}
vendors={vendors}
viewOnly
frozenCompany={source.frozenCompany}
onSave={() => undefined}
/>
);
}
export function SlideOverInfoTabDisplayView({
source,
isCancelled,
@ -41,11 +186,7 @@ export function SlideOverInfoTabDisplayView({
vendors,
pmTypes,
}: SlideOverInfoTabDisplayViewProps) {
const appt = parseApptTime(source.apptTime);
const apptDisplay = appt.start ? formatApptRange(appt.start, appt.end) : source.apptTime || "—";
const company = effectiveCompany(source);
const poc = effectivePoc(source);
const primaryPoc = poc.contacts[0];
return (
<div style={isCancelled ? { opacity: 0.6, pointerEvents: "none" } : undefined}>
@ -53,38 +194,10 @@ export function SlideOverInfoTabDisplayView({
<span className="font-mono text-[12.5px] font-semibold">{source.woNumber || "—"}</span>
</SlideOverInfoRow>
<SlideOverInfoRow label="Site">
{isCompleted ? (
<SiteCell
value={source.site}
location={source.location}
locationId={source.locationId}
pocName={primaryPoc?.name ?? ""}
pocPhone={primaryPoc?.phone ?? ""}
pocNotes={poc.notes ?? ""}
additionalContacts={poc.contacts.slice(1).map((contact) => ({
name: contact.name ?? "",
phone: contact.phone ?? "",
}))}
sites={sites}
viewOnly
frozenSite={source.frozenSite}
onSave={() => undefined}
/>
) : (
<span
className="font-mono text-[12.5px] font-bold"
style={{ color: "var(--foreground)" }}
>
{source.site || "—"}
</span>
)}
<SlideOverSiteField source={source} isCompleted={isCompleted} poc={poc} sites={sites} />
</SlideOverInfoRow>
<SlideOverInfoRow label="Type of WO">
{source.type ? (
<WoTableTypeLabel type={source.type as WOType} />
) : (
<span style={{ color: "var(--color-text-muted)" }}>—</span>
)}
<SlideOverTypeField type={source.type} />
</SlideOverInfoRow>
<SlideOverInfoRow label="Assigned To">
<SlideOverInfoTabAssignedField
@ -93,58 +206,19 @@ export function SlideOverInfoTabDisplayView({
/>
</SlideOverInfoRow>
<SlideOverInfoRow label="Schedule On">
<span className="text-[12.5px]">
{source.scheduleWeekOnly && source.targetWeek
? `Week of ${source.targetWeek}`
: source.scheduledOn || "—"}
</span>
<span className="text-[12.5px]">{formatScheduleOn(source)}</span>
</SlideOverInfoRow>
<SlideOverInfoRow label="Due Date">
<span className="text-[12.5px]">{source.dueDate || "—"}</span>
</SlideOverInfoRow>
<SlideOverInfoRow label="Appt Time">
<span className="text-[12.5px]">{apptDisplay}</span>
<span className="text-[12.5px]">{formatApptDisplay(source.apptTime)}</span>
</SlideOverInfoRow>
<SlideOverInfoRow label="Service">
{isCompleted ? (
<PMTypeCell
value={source.pm}
pmTypes={pmTypes}
serviceNotes={source.serviceNotes}
extraServices={source.extraServices}
avetaRequired={source.avetaRequired}
avetaAttached={source.hasAvetaDocument}
woNumber={source.woNumber}
viewOnly
onSave={() => undefined}
/>
) : source.pm ? (
<span className="inline-flex items-center gap-1.5 text-[12.5px]">
<PmTypeIcon pmType={source.pm} size={14} />
{source.pm}
{Boolean(source.avetaRequired) && (
<AvetaBadge size={11} attached={Boolean(source.hasAvetaDocument)} />
)}
</span>
) : (
<span style={{ color: "var(--color-text-muted)" }}>—</span>
)}
<SlideOverServiceField source={source} isCompleted={isCompleted} pmTypes={pmTypes} />
</SlideOverInfoRow>
<SlideOverInfoRow label="Vendor">
{isCompleted ? (
<VendorCell
vendorId={source.vendorId}
company={company.name ?? source.company}
tech={company.contactName ?? source.tech}
techPhone={company.phone ?? source.techPhone}
vendors={vendors}
viewOnly
frozenCompany={source.frozenCompany}
onSave={() => undefined}
/>
) : (
<SlideOverInfoTabVendorField source={source} />
)}
<SlideOverVendorField source={source} isCompleted={isCompleted} vendors={vendors} />
</SlideOverInfoRow>
<SlideOverInfoRow label="Status">
<SlideOverInfoTabStatusField source={source} />
@ -154,8 +228,7 @@ export function SlideOverInfoTabDisplayView({
</SlideOverInfoRow>
<SlideOverInfoRow label="POC">
<span className="text-[12.5px]" style={{ color: "var(--color-text-secondary)" }}>
{primaryPoc?.name || "—"}
{primaryPoc?.phone ? ` · ${primaryPoc.phone}` : ""}
{formatPocLine(poc)}
</span>
</SlideOverInfoRow>
</div>

View file

@ -10,6 +10,16 @@ type VendorDialogCompanyCardProps = {
frozenCompany?: WorkOrderFrozenCompany;
};
type CompanyCardModel = {
title: string;
distance: string;
contactName: string;
email: string;
address: string;
mapsUrl: string;
phone: string;
};
function formatFrozenCompanyAddress(company: WorkOrderFrozenCompany): string {
const region = [company.city, company.state]
.map((part) => part?.trim())
@ -19,21 +29,68 @@ function formatFrozenCompanyAddress(company: WorkOrderFrozenCompany): string {
return [company.address?.trim(), region, company.zip?.trim()].filter(Boolean).join(", ");
}
function liveMapsUrl(address: string): string {
if (!address) return "";
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}`;
}
function resolveFrozenCompanyCard(
company: string,
frozen: WorkOrderFrozenCompany,
): CompanyCardModel {
return {
title: company,
distance: "",
contactName: frozen.contactName?.trim() ?? "",
email: frozen.email?.trim() ?? "",
address: formatFrozenCompanyAddress(frozen),
mapsUrl: frozen.maps ?? "",
phone: frozen.phone ?? "",
};
}
function resolveLiveCompanyCard(
company: string,
vendor: VendorDropdownItem | undefined,
): CompanyCardModel {
const address = vendor?.address ?? "";
return {
title: company,
distance: formatVendorDistance(vendor?.distanceMiles),
contactName: "",
email: vendor?.email?.trim() ?? "",
address,
mapsUrl: liveMapsUrl(address),
phone: "",
};
}
function resolveCompanyCardModel(
company: string,
vendor: VendorDropdownItem | undefined,
frozenCompany?: WorkOrderFrozenCompany,
): CompanyCardModel {
if (frozenCompany != null) return resolveFrozenCompanyCard(company, frozenCompany);
return resolveLiveCompanyCard(company, vendor);
}
function CompanyCardDetails({ model }: { model: CompanyCardModel }) {
return (
<>
{Boolean(model.address) && <Detail icon={MapPin} text={model.address} />}
{Boolean(model.contactName) && <Detail icon={User} text={model.contactName} />}
{Boolean(model.phone) && <Detail icon={Phone} text={model.phone} />}
{Boolean(model.email) && <Detail icon={Mail} text={model.email} />}
</>
);
}
export function VendorDialogCompanyCard({
company,
vendor,
frozenCompany,
}: VendorDialogCompanyCardProps) {
const isFrozen = frozenCompany != null;
const distance = isFrozen ? "" : formatVendorDistance(vendor?.distanceMiles);
const contactName = isFrozen ? (frozenCompany.contactName?.trim() ?? "") : "";
const email = isFrozen ? (frozenCompany.email?.trim() ?? "") : (vendor?.email?.trim() ?? "");
const address = isFrozen ? formatFrozenCompanyAddress(frozenCompany) : (vendor?.address ?? "");
const mapsUrl =
(isFrozen ? frozenCompany.maps : undefined) ||
(!isFrozen && address
? `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}`
: "");
const model = resolveCompanyCardModel(company, vendor, frozenCompany);
return (
<div
@ -45,21 +102,18 @@ export function VendorDialogCompanyCard({
>
<div className="flex items-start justify-between gap-2">
<div style={{ fontSize: 13, fontWeight: 500, color: "var(--foreground)" }}>
{company || "—"}
{model.title || "—"}
</div>
{Boolean(distance) && (
{Boolean(model.distance) && (
<span className="shrink-0 text-[11px]" style={{ color: "var(--muted-foreground)" }}>
{distance}
{model.distance}
</span>
)}
</div>
{Boolean(address) && <Detail icon={MapPin} text={address} />}
{Boolean(contactName) && <Detail icon={User} text={contactName} />}
{Boolean(frozenCompany?.phone) && <Detail icon={Phone} text={frozenCompany?.phone ?? ""} />}
{Boolean(email) && <Detail icon={Mail} text={email} />}
{Boolean(mapsUrl) && (
<CompanyCardDetails model={model} />
{Boolean(model.mapsUrl) && (
<a
href={mapsUrl}
href={model.mapsUrl}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1.5 text-[12px] hover:underline"

View file

@ -7,37 +7,45 @@ function buildLabel(city?: string, state?: string): string {
return [city, state].filter(Boolean).join(", ");
}
function toCompDocSite(street: string, city: string, state: string, zip: string): CompDocSiteInfo {
const label = buildLabel(city, state);
const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
return { street, city, state, zip, label, fullAddress };
}
function resolveLiveLocationSite(location: Location): CompDocSiteInfo {
return toCompDocSite(
location.address ?? "",
location.city ?? "",
location.state ?? "",
location.zipCode ?? "",
);
}
function resolveRowLocationSite(row: Pick<WorkOrderTableRow, "location">): CompDocSiteInfo {
const locationParts = row.location.split(",").map((part) => part.trim());
const city = locationParts[0] ?? "";
const state = locationParts[1] ?? "";
const label = buildLabel(city, state);
const street = row.location && !label ? row.location : "";
return toCompDocSite(street, city, state, "");
}
export function resolveCompDocSite(
row: Pick<WorkOrderTableRow, "site" | "location" | "status" | "frozenSite">,
location?: Location | null,
): CompDocSiteInfo {
const frozen = effectiveSite(row);
if (frozen != null) {
const city = frozen.city ?? "";
const state = frozen.state ?? "";
const zip = frozen.zip ?? "";
const label = buildLabel(city, state);
const street = frozen.address ?? "";
const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
return { street, city, state, zip, label, fullAddress };
return toCompDocSite(
frozen.address ?? "",
frozen.city ?? "",
frozen.state ?? "",
frozen.zip ?? "",
);
}
if (location?.address || location?.city) {
const city = location.city ?? "";
const state = location.state ?? "";
const zip = location.zipCode ?? "";
const label = buildLabel(city, state);
const street = location.address ?? "";
const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
return { street, city, state, zip, label, fullAddress };
return resolveLiveLocationSite(location);
}
const locationParts = row.location.split(",").map((s) => s.trim());
const city = locationParts[0] ?? "";
const state = locationParts[1] ?? "";
const label = buildLabel(city, state);
const street = row.location && !label ? row.location : "";
const fullAddress = [street, label].filter(Boolean).join(", ") || "—";
return { street, city, state, zip: "", label, fullAddress };
return resolveRowLocationSite(row);
}