From 569b5ea3490c39faac571faab812ea146da274d1 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Thu, 16 Apr 2026 20:20:23 +0800 Subject: [PATCH] feat(scheduling): add frontend types and API client Co-Authored-By: Claude Sonnet 4.6 --- src/modules/scheduling/api.ts | 23 +++++++++++++ src/modules/scheduling/types.ts | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/modules/scheduling/api.ts create mode 100644 src/modules/scheduling/types.ts diff --git a/src/modules/scheduling/api.ts b/src/modules/scheduling/api.ts new file mode 100644 index 0000000..f67ed2e --- /dev/null +++ b/src/modules/scheduling/api.ts @@ -0,0 +1,23 @@ +import { fetchJson } from '../../auth/api-client'; +import type { SchedulingResponse } from './types'; + +const BASE = '/api/scheduling'; + +export async function fetchSuggestions(targetId?: number): Promise { + const params = new URLSearchParams(); + if (targetId !== undefined) params.set('targetId', String(targetId)); + const qs = params.toString(); + return fetchJson(`${BASE}/suggestions${qs ? `?${qs}` : ''}`); +} + +export async function sendNotify(body: { + suggestionId: string; + currentPlate: string; + candidatePlate: string; +}): Promise<{ success: boolean; message: string }> { + return fetchJson<{ success: boolean; message: string }>(`${BASE}/notify`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); +} diff --git a/src/modules/scheduling/types.ts b/src/modules/scheduling/types.ts new file mode 100644 index 0000000..7cbbe12 --- /dev/null +++ b/src/modules/scheduling/types.ts @@ -0,0 +1,58 @@ +export interface SchedulingVehicleInfo { + plateNumber: string; + targetId: number; + targetName: string; + vehicleType: string; + totalMileage: number; + completionRate: number; + yearTarget: number; + region: string; + province: string; + customer: string | null; + customerAvgDaily: number; + predictedYearEnd: number; + daysLeft: number; +} + +export interface CandidateVehicle { + plateNumber: string; + targetId: number | null; + targetName: string | null; + vehicleType: string; + totalMileage: number; + completionRate: number; + yearTarget: number | null; + region: string; + province: string; + mileageGap: number; + predictedAfterSwap: number; + canQualifyAfterSwap: boolean; +} + +export interface SchedulingSuggestion { + id: string; + priority: 'high' | 'medium'; + type: 'replace_qualified' | 'rescue_hopeless'; + currentVehicle: SchedulingVehicleInfo; + candidates: CandidateVehicle[]; + reason: string; +} + +export interface SchedulingSummary { + qualifiedCount: number; + hopelessCount: number; + suggestionCount: number; + estimatedGain: number; +} + +export interface SchedulingTargetOption { + id: number; + name: string; + vehicleCount: number; +} + +export interface SchedulingResponse { + summary: SchedulingSummary; + suggestions: SchedulingSuggestion[]; + targets: SchedulingTargetOption[]; +}