import { API_PATHS } from "@/api/api-paths"; import { apiGet } from "@/api/api"; import { handleApiResponse } from "@/api/handle-api-response"; import type { Options } from "ky"; import type { DashboardRangeParams } from "@/domain/dashboard/types/dashboard-range"; import type { DashboardBreakdownRow, DashboardStats, } from "@/domain/dashboard/types/dashboard-stats"; import type { DispatcherPerformanceRow } from "@/domain/dashboard/types/dashboard-performance"; import type { DashboardTrendParams } from "@/domain/dashboard/types/dashboard-trend"; import type { DashboardTrend, DashboardTrendGranularity, } from "@/domain/dashboard/types/dashboard-trend"; import { UNMAPPED_REGION_LABEL, type RegionWorkOrdersRow, } from "@/domain/dashboard/types/dashboard-regions"; import type { DispatcherWorkloadRow } from "@/domain/dashboard/types/dashboard-workload"; import type { VendorInsightRow } from "@/domain/dashboard/types/dashboard-vendor-insights"; function toNumber(value: unknown): number { if (typeof value === "number" && Number.isFinite(value)) { return value; } if (typeof value === "string" && value.trim() !== "") { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : 0; } return 0; } function asString(value: unknown): string { if (typeof value === "string") { return value.trim(); } if (typeof value === "number" && Number.isFinite(value)) { return String(value); } return ""; } function asRecords(value: unknown): Record[] { if (!Array.isArray(value)) { return []; } return value.filter( (item): item is Record => typeof item === "object" && item !== null && !Array.isArray(item), ); } function readField(data: Record, key: string): unknown { const capitalized = key.charAt(0).toUpperCase() + key.slice(1); const value = data[key] ?? data[capitalized]; return value === null ? undefined : value; } function toRecord(value: unknown): Record { return typeof value === "object" && value !== null && !Array.isArray(value) ? (value as Record) : {}; } function readCollection(value: unknown, key: string): Record[] { const data = toRecord(value); const collection = key ? (readField(data, key) ?? value) : value; return asRecords(collection); } function rangeSearchParams(range?: DashboardRangeParams): Options | undefined { if (!range?.dateFrom || !range?.dateTo) { return undefined; } return { searchParams: { dateFrom: range.dateFrom, dateTo: range.dateTo } }; } function trendSearchParams(params?: DashboardTrendParams): Options | undefined { if ( !params || (!params.dateFrom && !params.dateTo && !params.granularity && !params.range && !params.year) ) { return undefined; } const searchParams: Record = {}; if (params.dateFrom) searchParams.dateFrom = params.dateFrom; if (params.dateTo) searchParams.dateTo = params.dateTo; if (params.granularity) searchParams.granularity = params.granularity; if (params.range) searchParams.range = params.range; if (params.year) searchParams.year = params.year; return { searchParams }; } function mapBreakdownRow(raw: Record): DashboardBreakdownRow { return { status: asString(readField(raw, "status") ?? readField(raw, "name") ?? readField(raw, "label")) || UNMAPPED_REGION_LABEL, count: toNumber(readField(raw, "count") ?? readField(raw, "value")), }; } function mapBreakdown(value: unknown): DashboardBreakdownRow[] { if (Array.isArray(value)) { return asRecords(value).map(mapBreakdownRow); } if (typeof value === "object" && value !== null) { return Object.entries(value as Record) .map(([status, count]) => ({ status, count: toNumber(count) })) .filter((row) => row.status !== ""); } return []; } function mapDispatcherIdentity(raw: Record): { dispatcherId: string; dispatcherName: string; } { return { dispatcherId: asString( readField(raw, "dispatcherId") ?? readField(raw, "id") ?? readField(raw, "userId"), ), dispatcherName: asString(readField(raw, "dispatcherName") ?? readField(raw, "name")), }; } function mapDashboardStats(raw: unknown): DashboardStats { const data = toRecord(raw); const average = readField(data, "averageResolutionDays"); return { total: toNumber(readField(data, "total")), open: toNumber(readField(data, "open")), notDispatched: toNumber(readField(data, "notDispatched")), completed: toNumber(readField(data, "completed")), dueCount: toNumber(readField(data, "dueCount")), completedDueCount: toNumber(readField(data, "completedDueCount")), completionRate: toNumber(readField(data, "completionRate")), averageResolutionDays: average == null ? null : toNumber(average), scheduledTomorrow: toNumber(readField(data, "scheduledTomorrow")), pendingUplifts: toNumber(readField(data, "pendingUplifts")), avetaPending: toNumber(readField(data, "avetaPending")), breakdown: mapBreakdown(readField(data, "breakdown") ?? readField(data, "workOrderBreakdown")), }; } function mapDispatcherWorkload(raw: unknown): DispatcherWorkloadRow[] { return readCollection(raw, "items").map((rawRow) => { const identity = mapDispatcherIdentity(rawRow); return { ...identity, openWorkOrders: toNumber( readField(rawRow, "openWorkOrders") ?? readField(rawRow, "openCount") ?? readField(rawRow, "open"), ), totalWorkOrders: toNumber( readField(rawRow, "totalWorkOrders") ?? readField(rawRow, "totalCount") ?? readField(rawRow, "scheduledWorkOrders") ?? readField(rawRow, "scheduled"), ), }; }); } function mapDispatcherPerformance(raw: unknown): DispatcherPerformanceRow[] { return readCollection(raw, "items").map((rawRow) => { const identity = mapDispatcherIdentity(rawRow); return { ...identity, completionRate: toNumber( readField(rawRow, "completionRate") ?? readField(rawRow, "onTimeRate"), ), averageResolutionDays: readField(rawRow, "averageResolutionDays") == null ? null : toNumber(readField(rawRow, "averageResolutionDays")), }; }); } function mapRegions(raw: unknown): RegionWorkOrdersRow[] { return readCollection(raw, "items").map((rawRow) => { const region = asString(readField(rawRow, "region") ?? readField(rawRow, "name")) || UNMAPPED_REGION_LABEL; return { region, workOrderCount: toNumber( readField(rawRow, "workOrderCount") ?? readField(rawRow, "count") ?? readField(rawRow, "total"), ), }; }); } function mapVendorInsights(raw: unknown): VendorInsightRow[] { return readCollection(raw, "items").map((rawRow) => ({ vendorId: asString( readField(rawRow, "vendorId") ?? readField(rawRow, "vendorCompanyId") ?? readField(rawRow, "id"), ), vendorName: asString( readField(rawRow, "vendorName") ?? readField(rawRow, "vendorCompanyName") ?? readField(rawRow, "companyName") ?? readField(rawRow, "name"), ), completionRate: toNumber(readField(rawRow, "completionRate")), rescheduleRate: toNumber(readField(rawRow, "rescheduleRate")), averageResolutionDays: readField(rawRow, "averageResolutionDays") == null ? null : toNumber(readField(rawRow, "averageResolutionDays")), totalJobs: toNumber(readField(rawRow, "totalJobs")), })); } function mapTrendGranularity(value: unknown): DashboardTrendGranularity | null { const granularity = asString(value).toLowerCase(); return granularity === "day" || granularity === "week" || granularity === "month" ? granularity : null; } function mapDashboardTrend(raw: unknown): DashboardTrend { const data = toRecord(raw); const buckets = readCollection(data, "buckets"); const legacyPoints = readCollection(data, "points"); const points = (buckets.length > 0 ? buckets : legacyPoints).map((rawPoint) => ({ date: asString(readField(rawPoint, "date")), label: asString( readField(rawPoint, "label") ?? readField(rawPoint, "period") ?? readField(rawPoint, "date"), ), total: toNumber( readField(rawPoint, "total") ?? readField(rawPoint, "count") ?? readField(rawPoint, "value"), ), open: toNumber(readField(rawPoint, "open")), completed: toNumber(readField(rawPoint, "completed")), canceled: toNumber(readField(rawPoint, "canceled")), overdue: toNumber(readField(rawPoint, "overdue")), isCurrent: readField(rawPoint, "isCurrent") === true, })); return { granularity: mapTrendGranularity(readField(data, "granularity")), points }; } export const dashboardApi = { getStats: async (range?: DashboardRangeParams): Promise => { const data = await apiGet(API_PATHS.dashboard.stats, rangeSearchParams(range)); return mapDashboardStats(handleApiResponse(data)); }, getWorkload: async (range?: DashboardRangeParams): Promise => { const data = await apiGet(API_PATHS.dashboard.workload, rangeSearchParams(range)); return mapDispatcherWorkload(handleApiResponse(data)); }, getPerformance: async (range?: DashboardRangeParams): Promise => { const data = await apiGet(API_PATHS.dashboard.performance, rangeSearchParams(range)); return mapDispatcherPerformance(handleApiResponse(data)); }, getRegions: async (range?: DashboardRangeParams): Promise => { const data = await apiGet(API_PATHS.dashboard.regions, rangeSearchParams(range)); return mapRegions(handleApiResponse(data)); }, getVendorInsights: async (): Promise => { const data = await apiGet(API_PATHS.dashboard.vendorInsights); return mapVendorInsights(handleApiResponse(data)); }, getTrend: async (params?: DashboardTrendParams): Promise => { const data = await apiGet(API_PATHS.dashboard.trend, trendSearchParams(params)); return mapDashboardTrend(handleApiResponse(data)); }, };