shoc-frontend-new/src/domain/work-orders/utils/dashboard-drilldown-params.ts
Alexandre Brandizzi 2281c7c82d fix(sites): open the site's board when open work orders exceed the id cap
The delete dialog states the server's full open work-order count, but the
server returns at most 200 ids. "View open work orders" navigated with
that capped list, so a site with 240 open work orders showed 200, and an
empty list opened the unfiltered board.

The exact-id link is now used only when the ids cover the whole count.
Otherwise the link opens Work Orders filtered to the site and every open
status across all weeks, the ticket's "board filtered to that site". The
board drilldown now reads a `sites` param for this; `ids` still wins.
2026-09-25 17:10:51 -03:00

147 lines
5.1 KiB
TypeScript

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";
import { parseWorkOrderIdsParam } from "@/domain/work-orders/utils/work-order-ids-filter";
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const COMPLETION_DOC_FILTERS: CompletionDocFilter[] = ["No", "Yes", "NN"];
// SH-348: the board region filter (backend#146) accepts these region names.
const BOARD_REGION_NAMES = ["East", "Central", "West", "California", "Unmapped/Other"];
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));
}
function parseRegions(raw: string | null): string[] {
const valid = new Set<string>(BOARD_REGION_NAMES);
return [...new Set(parseList(raw))].filter((region) => valid.has(region));
}
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),
);
}
/** Open = every lifecycle status except Completed (Canceled is not a board status). */
const OPEN_STATUSES = ALL_WIZARD_STATUSES.filter((status) => status !== "Completed");
/** Work Orders filtered to one site's open work orders; `sites` takes the location id. */
export function buildSiteOpenWorkOrdersHref(siteId: string | number): string {
const params = new URLSearchParams({
sites: String(siteId),
statuses: OPEN_STATUSES.join(","),
});
return `/workorders?${params.toString()}`;
}
export function parseDashboardDrilldownParams(
params: URLSearchParams,
): WorkOrderAdvancedFilters | null {
// 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) };
}
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"));
const regions = parseRegions(params.get("regions"));
const sites = [...new Set(parseList(params.get("sites")))];
const upliftStatuses = parseUpliftStatuses(params.get("upliftStatuses"));
const hasUplift = params.get("hasUplift") === "1";
const hasAny = [
hasDateRange,
avetaOnly,
docs.length,
statuses.length,
types.length,
dispatchers.length,
regions.length,
sites.length,
upliftStatuses.length,
hasUplift,
].some(Boolean);
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);
}
if (regions.length > 0) {
filters.regions = new Set(regions);
}
if (sites.length > 0) {
filters.sites = new Set(sites);
}
if (upliftStatuses.length > 0) {
filters.upliftStatuses = new Set(upliftStatuses);
}
return filters;
}