2026-09-16 19:37:15 -03:00
|
|
|
import type { CompletionDocFilter } from "@/domain/work-orders/types/work-order-advanced-filters";
|
|
|
|
|
import type { WorkOrderAdvancedFilters } from "@/domain/work-orders/types/work-order-advanced-filters";
|
|
|
|
|
import { EMPTY_ADVANCED_FILTERS } from "@/domain/work-orders/types/work-order-advanced-filters";
|
|
|
|
|
import { ALL_WIZARD_STATUSES, WO_TYPES } from "@/domain/work-orders/types/work-order-wizard";
|
|
|
|
|
import type { WizardWOStatus, WOType } from "@/domain/work-orders/types/work-order-wizard";
|
|
|
|
|
import type { UpliftFilterStatus } from "@/domain/work-orders/types/work-order-uplift-filter";
|
|
|
|
|
import { cloneAdvancedFilters } from "@/domain/work-orders/utils/advanced-filters-utils";
|
2026-09-25 11:06:09 -03:00
|
|
|
import { parseWorkOrderIdsParam } from "@/domain/work-orders/utils/work-order-ids-filter";
|
2026-09-16 19:37:15 -03:00
|
|
|
|
|
|
|
|
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
|
|
|
|
|
|
|
const COMPLETION_DOC_FILTERS: CompletionDocFilter[] = ["No", "Yes", "NN"];
|
|
|
|
|
|
feat(dashboard): dispatcher scope picker, status distribution, region drilldown, perf counts
- SH-336: add the dispatcher-scope picker (All / My WOs / individual) for
Scheduler/Admin via a new viewAllDispatchersOnDashboard role gate, a static
'My WOs' label for Dispatcher, and hide the dispatcher tables (and skip their
queries) when the viewer cannot see all dispatchers. Scope drives dispatcherId
on stats, workload, performance, and regions (SH-347 contract).
- SH-352: add the Status Distribution module with its empty state and status
drill-down.
- SH-347: show Assigned/Completed counts and colour the completion rate
(green >=90, amber 70-89, red <70) on Dispatcher Performance.
- SH-348: drill a region bar into the board region filter; hold the
unconfirmed 'Unmapped/Other' bucket off the chart.
- SH-349: label Vendor Insights as all-time, company-wide.
- Make the drilldown-filters effect fire only on URL change; add a deterministic
clock to the status-drilldown test; add the dashboard visual regression spec
and baselines.
2026-09-17 02:56:31 -03:00
|
|
|
// SH-348: the board region filter (backend#146) accepts these region names.
|
|
|
|
|
const BOARD_REGION_NAMES = ["East", "Central", "West", "California", "Unmapped/Other"];
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
const UPLIFT_FILTER_VALUES: UpliftFilterStatus[] = [
|
|
|
|
|
"pending",
|
|
|
|
|
"approved",
|
|
|
|
|
"auto_approved",
|
|
|
|
|
"rejected",
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function parseList(raw: string | null): string[] {
|
|
|
|
|
if (!raw) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return raw
|
|
|
|
|
.split(",")
|
|
|
|
|
.map((item) => item.trim())
|
|
|
|
|
.filter((item) => item !== "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseStatuses(raw: string | null): WizardWOStatus[] {
|
|
|
|
|
const valid = new Set<string>(ALL_WIZARD_STATUSES);
|
|
|
|
|
return [...new Set(parseList(raw))].filter((status): status is WizardWOStatus =>
|
|
|
|
|
valid.has(status),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseTypes(raw: string | null): WOType[] {
|
|
|
|
|
const valid = new Set<string>(WO_TYPES);
|
|
|
|
|
return [...new Set(parseList(raw))].filter((type): type is WOType => valid.has(type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseCompletionDocs(raw: string | null): CompletionDocFilter[] {
|
|
|
|
|
const valid = new Set<string>(COMPLETION_DOC_FILTERS);
|
|
|
|
|
return [...new Set(parseList(raw))].filter((doc): doc is CompletionDocFilter => valid.has(doc));
|
|
|
|
|
}
|
|
|
|
|
|
feat(dashboard): dispatcher scope picker, status distribution, region drilldown, perf counts
- SH-336: add the dispatcher-scope picker (All / My WOs / individual) for
Scheduler/Admin via a new viewAllDispatchersOnDashboard role gate, a static
'My WOs' label for Dispatcher, and hide the dispatcher tables (and skip their
queries) when the viewer cannot see all dispatchers. Scope drives dispatcherId
on stats, workload, performance, and regions (SH-347 contract).
- SH-352: add the Status Distribution module with its empty state and status
drill-down.
- SH-347: show Assigned/Completed counts and colour the completion rate
(green >=90, amber 70-89, red <70) on Dispatcher Performance.
- SH-348: drill a region bar into the board region filter; hold the
unconfirmed 'Unmapped/Other' bucket off the chart.
- SH-349: label Vendor Insights as all-time, company-wide.
- Make the drilldown-filters effect fire only on URL change; add a deterministic
clock to the status-drilldown test; add the dashboard visual regression spec
and baselines.
2026-09-17 02:56:31 -03:00
|
|
|
function parseRegions(raw: string | null): string[] {
|
|
|
|
|
const valid = new Set<string>(BOARD_REGION_NAMES);
|
|
|
|
|
return [...new Set(parseList(raw))].filter((region) => valid.has(region));
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
function parseUpliftStatuses(raw: string | null): UpliftFilterStatus[] {
|
|
|
|
|
const valid = new Set<string>(UPLIFT_FILTER_VALUES);
|
|
|
|
|
return [...new Set(parseList(raw))].filter((value): value is UpliftFilterStatus =>
|
|
|
|
|
valid.has(value),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseDashboardDrilldownParams(
|
|
|
|
|
params: URLSearchParams,
|
|
|
|
|
): WorkOrderAdvancedFilters | null {
|
2026-09-25 11:06:09 -03:00
|
|
|
// An exact id set replaces every other filter: nothing else may hide one of those work orders.
|
|
|
|
|
const workOrderIds = parseWorkOrderIdsParam(params.get("ids"));
|
|
|
|
|
if (workOrderIds.length > 0) {
|
|
|
|
|
return { ...cloneAdvancedFilters(EMPTY_ADVANCED_FILTERS), workOrderIds: new Set(workOrderIds) };
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:37:15 -03:00
|
|
|
const dateFrom = (params.get("dateFrom") ?? "").trim();
|
|
|
|
|
const dateTo = (params.get("dateTo") ?? "").trim();
|
|
|
|
|
const hasDateRange = ISO_DATE_PATTERN.test(dateFrom) && ISO_DATE_PATTERN.test(dateTo);
|
|
|
|
|
const avetaOnly = params.get("aveta") === "1";
|
|
|
|
|
const docs = parseCompletionDocs(params.get("docs"));
|
|
|
|
|
const statuses = parseStatuses(params.get("statuses"));
|
|
|
|
|
const types = parseTypes(params.get("types"));
|
|
|
|
|
const dispatchers = parseList(params.get("dispatchers"));
|
feat(dashboard): dispatcher scope picker, status distribution, region drilldown, perf counts
- SH-336: add the dispatcher-scope picker (All / My WOs / individual) for
Scheduler/Admin via a new viewAllDispatchersOnDashboard role gate, a static
'My WOs' label for Dispatcher, and hide the dispatcher tables (and skip their
queries) when the viewer cannot see all dispatchers. Scope drives dispatcherId
on stats, workload, performance, and regions (SH-347 contract).
- SH-352: add the Status Distribution module with its empty state and status
drill-down.
- SH-347: show Assigned/Completed counts and colour the completion rate
(green >=90, amber 70-89, red <70) on Dispatcher Performance.
- SH-348: drill a region bar into the board region filter; hold the
unconfirmed 'Unmapped/Other' bucket off the chart.
- SH-349: label Vendor Insights as all-time, company-wide.
- Make the drilldown-filters effect fire only on URL change; add a deterministic
clock to the status-drilldown test; add the dashboard visual regression spec
and baselines.
2026-09-17 02:56:31 -03:00
|
|
|
const regions = parseRegions(params.get("regions"));
|
2026-09-16 19:37:15 -03:00
|
|
|
const upliftStatuses = parseUpliftStatuses(params.get("upliftStatuses"));
|
|
|
|
|
const hasUplift = params.get("hasUplift") === "1";
|
|
|
|
|
|
2026-09-17 03:14:15 -03:00
|
|
|
const hasAny = [
|
|
|
|
|
hasDateRange,
|
|
|
|
|
avetaOnly,
|
|
|
|
|
docs.length,
|
|
|
|
|
statuses.length,
|
|
|
|
|
types.length,
|
|
|
|
|
dispatchers.length,
|
|
|
|
|
regions.length,
|
|
|
|
|
upliftStatuses.length,
|
|
|
|
|
hasUplift,
|
|
|
|
|
].some(Boolean);
|
2026-09-16 19:37:15 -03:00
|
|
|
|
|
|
|
|
if (!hasAny) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const filters = cloneAdvancedFilters(EMPTY_ADVANCED_FILTERS);
|
|
|
|
|
|
|
|
|
|
if (hasDateRange) {
|
|
|
|
|
filters.dateRange = "custom";
|
|
|
|
|
filters.customFrom = dateFrom;
|
|
|
|
|
filters.customTo = dateTo;
|
|
|
|
|
}
|
|
|
|
|
filters.avetaOnly = avetaOnly;
|
|
|
|
|
filters.hasUplift = hasUplift || upliftStatuses.length > 0;
|
|
|
|
|
if (docs.length > 0) {
|
|
|
|
|
filters.docs = new Set(docs);
|
|
|
|
|
}
|
|
|
|
|
if (statuses.length > 0) {
|
|
|
|
|
filters.statuses = new Set(statuses);
|
|
|
|
|
}
|
|
|
|
|
if (types.length > 0) {
|
|
|
|
|
filters.types = new Set(types);
|
|
|
|
|
}
|
|
|
|
|
if (dispatchers.length > 0) {
|
|
|
|
|
filters.dispatchers = new Set(dispatchers);
|
|
|
|
|
}
|
feat(dashboard): dispatcher scope picker, status distribution, region drilldown, perf counts
- SH-336: add the dispatcher-scope picker (All / My WOs / individual) for
Scheduler/Admin via a new viewAllDispatchersOnDashboard role gate, a static
'My WOs' label for Dispatcher, and hide the dispatcher tables (and skip their
queries) when the viewer cannot see all dispatchers. Scope drives dispatcherId
on stats, workload, performance, and regions (SH-347 contract).
- SH-352: add the Status Distribution module with its empty state and status
drill-down.
- SH-347: show Assigned/Completed counts and colour the completion rate
(green >=90, amber 70-89, red <70) on Dispatcher Performance.
- SH-348: drill a region bar into the board region filter; hold the
unconfirmed 'Unmapped/Other' bucket off the chart.
- SH-349: label Vendor Insights as all-time, company-wide.
- Make the drilldown-filters effect fire only on URL change; add a deterministic
clock to the status-drilldown test; add the dashboard visual regression spec
and baselines.
2026-09-17 02:56:31 -03:00
|
|
|
if (regions.length > 0) {
|
|
|
|
|
filters.regions = new Set(regions);
|
|
|
|
|
}
|
2026-09-16 19:37:15 -03:00
|
|
|
if (upliftStatuses.length > 0) {
|
|
|
|
|
filters.upliftStatuses = new Set(upliftStatuses);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filters;
|
|
|
|
|
}
|