2b1d7c0b65
The dashboard had several oversized orchestration, component, and CSS files that made product-copy changes and mobile/performance work risky. This refactor preserves behavior while splitting scan terminal CSS, opportunity helpers, future forecast panels, history/detail charts, and probability/model sections into smaller ownership boundaries. Constraint: No user-visible version bump because this batch is architecture and performance cleanup, not a release announcement. Rejected: Rewrite dashboard state management in the same batch | too broad for a safe upload after CSS and component splitting. Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep new component/CSS boundaries instead of moving product copy back into the large dashboard files. Tested: npm run build; npm run test:business; git diff --check Not-tested: Browser visual smoke test after push
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
import type { CityDetail, ScanOpportunityRow } from "@/lib/dashboard-types";
|
|
|
|
export function normalizeLookupKey(value?: string | null) {
|
|
return String(value || "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[\s_-]+/g, "");
|
|
}
|
|
|
|
export function getDetailForRow(
|
|
row: Pick<ScanOpportunityRow, "city" | "city_display_name" | "display_name">,
|
|
cityDetailsByName?: Record<string, CityDetail>,
|
|
) {
|
|
if (!cityDetailsByName) return null;
|
|
const rowKeys = [row.city, row.city_display_name, row.display_name]
|
|
.map(normalizeLookupKey)
|
|
.filter(Boolean);
|
|
return (
|
|
Object.entries(cityDetailsByName).find(([name, detail]) => {
|
|
const detailKeys = [name, detail.name, detail.display_name]
|
|
.map(normalizeLookupKey)
|
|
.filter(Boolean);
|
|
return rowKeys.some((key) => detailKeys.includes(key));
|
|
})?.[1] || null
|
|
);
|
|
}
|
|
|
|
export function getDetailViewDate(detail: CityDetail, row?: ScanOpportunityRow | null) {
|
|
if (!row) return detail.local_date;
|
|
const rawDate = row.selected_date || row.local_date || "";
|
|
const phase = String(row.window_phase || "").toLowerCase();
|
|
if ((phase === "tomorrow" || phase === "week_ahead") && rawDate) return rawDate;
|
|
if (!rawDate || rawDate === detail.local_date || row.local_date === detail.local_date) {
|
|
return detail.local_date;
|
|
}
|
|
return detail.local_date || rawDate;
|
|
}
|