118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
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<MileageBreakdownDimension>([
|
|
'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}`;
|
|
}
|