shoc-frontend-new/src/domain/work-orders/utils/format-board-audit-action.ts
Arthur Bassi 6e99dc0af2 refactor(work-orders): pass board-api governance maintainability gate
Split oversized API/mapper/UI modules and extract focused helpers so the
exact head satisfies godfile and complexity ratchets without relaxing thresholds.
2026-07-28 09:56:27 -03:00

132 lines
4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
BOARD_AUDIT_ACTION_RULES,
wrapAuditValue,
type AuditActionSource,
type FormattedAuditValue,
} from "@/domain/work-orders/utils/board-audit-action-rules";
function looksLikeHumanSentence(action: string): boolean {
return /\s/.test(action);
}
function humanizeFieldLabel(fieldName: string): string {
const spaced = fieldName
.replace(/([a-z])([A-Z])/g, "$1 $2")
.replace(/[_-]+/g, " ")
.trim()
.toLowerCase();
return spaced || "field";
}
function normalizeKey(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/[\s_-]+/g, "");
}
/** JSON array strings → `"a", "b", "c"` instead of `["a","b","c"]`. */
function formatJsonArrayDisplayValue(raw: string): string | null {
const trimmed = raw.trim();
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
try {
const parsed: unknown = JSON.parse(trimmed);
if (!Array.isArray(parsed)) return null;
const items = parsed
.map((item) => String(item ?? "").trim())
.filter(Boolean)
.map((item) => `"${item}"`);
return items.join(", ");
} catch {
return null;
}
}
/** Parse ISO / date-only / pipe-separated API values → unique mm/dd/yyyy. */
function formatAuditDisplayValue(raw: string): FormattedAuditValue {
const trimmed = raw.trim();
if (!trimmed) return { text: trimmed, isQuotedList: false };
const jsonArray = formatJsonArrayDisplayValue(trimmed);
if (jsonArray !== null) return { text: jsonArray, isQuotedList: true };
const parts = trimmed
.split("|")
.map((part) => part.trim())
.filter(Boolean);
const dates: string[] = [];
for (const part of parts) {
const isoMatch = part.match(/^(\d{4})-(\d{2})-(\d{2})(?:T|$)/);
if (isoMatch) {
const [, year, month, day] = isoMatch;
dates.push(`${month}/${day}/${year}`);
continue;
}
const date = new Date(part);
if (!Number.isNaN(date.getTime()) && /\d{4}/.test(part)) {
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const year = String(date.getFullYear());
dates.push(`${month}/${day}/${year}`);
continue;
}
return { text: trimmed, isQuotedList: false };
}
const unique = [...new Set(dates)];
if (unique.length === 0) return { text: trimmed, isQuotedList: false };
if (unique.length === 1) return { text: unique[0]!, isQuotedList: false };
return { text: unique.join(" – "), isQuotedList: false };
}
/**
* Builds a design-style audit sentence (the part after `Name · `),
* e.g. `Changed status to "Incomplete"`, from board API fields.
*/
export function formatBoardAuditAction(entry: AuditActionSource): string {
const action = entry.action?.trim() ?? "";
const fieldName = entry.fieldName?.trim() ?? "";
const oldValue = formatAuditDisplayValue(entry.oldValue?.trim() ?? "");
const newValue = formatAuditDisplayValue(entry.newValue?.trim() ?? "");
if (action && looksLikeHumanSentence(action)) {
return action;
}
const fieldKey = normalizeKey(fieldName);
const actionKey = normalizeKey(action);
const ruleContext = { entry, fieldKey, actionKey, oldValue, newValue };
for (const rule of BOARD_AUDIT_ACTION_RULES) {
const result = rule(ruleContext);
if (result !== null) return result;
}
return formatGenericAuditFallback({ action, fieldName, oldValue, newValue });
}
function formatGenericAuditFallback(args: {
action: string;
fieldName: string;
oldValue: FormattedAuditValue;
newValue: FormattedAuditValue;
}): string {
const { action, fieldName, oldValue, newValue } = args;
if (!action && !fieldName && !oldValue.text && !newValue.text) {
return "Work order created in the system.";
}
const label = humanizeFieldLabel(fieldName || action || "field");
if (oldValue.text && newValue.text) {
return `Changed ${label} from ${wrapAuditValue(oldValue)} to ${wrapAuditValue(newValue)}`;
}
if (newValue.text) {
return `Changed ${label} to ${wrapAuditValue(newValue)}`;
}
if (action) return action;
return "Updated work order";
}