import type { MileageSourceGroup } from './types'; export type MileageDrillLevel = 'overview' | 'breakdown' | 'vehicle'; export type MileageBreakdownDimension = | 'department' | 'region' | 'customer' | 'vehicle-model' | 'assessment-target'; export interface MileageDrillContext { level: MileageDrillLevel; targetId?: number; dimension?: MileageBreakdownDimension; dimensionValue?: string; plate?: string; startDate?: string; endDate?: string; sourcePriority: MileageSourceGroup[]; } interface LocationParts { pathname: string; search: string; hash: string; } const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/; const BREAKDOWN_DIMENSIONS = new Set([ 'department', 'region', 'customer', 'vehicle-model', 'assessment-target', ]); function validDate(value: string | null): string | undefined { if (!value || !DATE_PATTERN.test(value)) return undefined; const timestamp = Date.parse(`${value}T00:00:00+08:00`); return Number.isFinite(timestamp) ? value : undefined; } function parseSourcePriority(value: string | null): MileageSourceGroup[] { if (!value) return ['instrument']; const sources = value .split(',') .filter((source): source is MileageSourceGroup => source === 'instrument' || source === 'gps'); return Array.from(new Set(sources)).slice(0, 2).length > 0 ? Array.from(new Set(sources)).slice(0, 2) : ['instrument']; } export function parseMileageDrillContext(search: string): MileageDrillContext { const params = new URLSearchParams(search); const rawTargetId = Number(params.get('mileageTarget')); const targetId = Number.isInteger(rawTargetId) && rawTargetId > 0 ? rawTargetId : undefined; const plate = params.get('mileagePlate')?.trim() || undefined; const rawDimension = params.get('mileageDimension'); const dimension = rawDimension && BREAKDOWN_DIMENSIONS.has(rawDimension as MileageBreakdownDimension) ? rawDimension as MileageBreakdownDimension : undefined; const dimensionValue = params.get('mileageValue')?.trim() || undefined; const requestedLevel = params.get('mileageLevel'); let level: MileageDrillLevel = 'overview'; if (requestedLevel === 'vehicle' && plate) level = 'vehicle'; if (requestedLevel === 'breakdown' && dimension && dimensionValue) level = 'breakdown'; const context: MileageDrillContext = { level, sourcePriority: parseSourcePriority(params.get('mileageSource')), }; if (targetId) context.targetId = targetId; if (level === 'breakdown') { context.dimension = dimension; context.dimensionValue = dimensionValue; } if (level === 'vehicle') context.plate = plate; const startDate = validDate(params.get('mileageStart')); const endDate = validDate(params.get('mileageEnd')); if (startDate) context.startDate = startDate; if (endDate) context.endDate = endDate; return context; } export function buildMileageDrillUrl( location: LocationParts, context: MileageDrillContext, ): string { const params = new URLSearchParams(location.search); for (const key of [ 'mileageLevel', 'mileageTarget', 'mileageDimension', 'mileageValue', 'mileagePlate', 'mileageStart', 'mileageEnd', 'mileageSource', ]) { params.delete(key); } if (context.level !== 'overview') params.set('mileageLevel', context.level); if (context.targetId) params.set('mileageTarget', String(context.targetId)); if (context.level === 'breakdown' && context.dimension && context.dimensionValue) { params.set('mileageDimension', context.dimension); params.set('mileageValue', context.dimensionValue); } if (context.level === 'vehicle' && context.plate) params.set('mileagePlate', context.plate); if (context.startDate) params.set('mileageStart', context.startDate); if (context.endDate) params.set('mileageEnd', context.endDate); if (context.sourcePriority.length > 0) params.set('mileageSource', context.sourcePriority.join(',')); const query = params.toString(); return `${location.pathname}${query ? `?${query}` : ''}${location.hash}`; }