import { z } from 'zod'; const API_URL = 'http://localhost:5000/api'; // --- Zod Schemas --- // Base schemas to handle circular references if needed, though usually mapped by API // defined separately to allow recursive type inference if we went down the z.lazy route, // but for now we'll keep it simple as the API returns what it returns. export const BaseVehicleGroupSchema = z.object({ id: z.number(), name: z.string(), description: z.string().optional().nullable(), }); export const VehicleSchema = z.object({ vin: z.string(), status: z.string(), currentVersion: z.string(), lastHeartbeat: z.string(), groupId: z.number().optional().nullable(), group: BaseVehicleGroupSchema.optional().nullable(), }); export const VehicleGroupSchema = BaseVehicleGroupSchema.extend({ vehicles: z.array(VehicleSchema).optional().nullable(), }); export const FirmwareUpdateSchema = z.object({ id: z.number(), version: z.string(), description: z.string().optional().nullable(), uploadedAt: z.string(), }); export const DeploymentSchema = z.object({ id: z.number(), updateId: z.number(), targetVin: z.string().optional().nullable(), targetGroupId: z.number().optional().nullable(), status: z.string(), createdAt: z.string(), update: FirmwareUpdateSchema, }); // --- Types Inferred from Schemas --- export type Vehicle = z.infer; export type VehicleGroup = z.infer; export type FirmwareUpdate = z.infer; export type Deployment = z.infer; // --- API Functions with Validation --- export async function fetchVehicles(): Promise { const res = await fetch(`${API_URL}/admin/vehicles`); if (!res.ok) throw new Error('Failed to fetch vehicles'); const json = await res.json(); return z.array(VehicleSchema).parse(json); } export async function fetchGroups(): Promise { const res = await fetch(`${API_URL}/admin/groups`); if (!res.ok) throw new Error('Failed to fetch groups'); const json = await res.json(); return z.array(VehicleGroupSchema).parse(json); } export async function getUpdates(): Promise { const res = await fetch(`${API_URL}/admin/updates`); if (!res.ok) throw new Error('Failed to fetch updates'); const json = await res.json(); return z.array(FirmwareUpdateSchema).parse(json); } export async function createGroup(name: string, description?: string): Promise { const res = await fetch(`${API_URL}/admin/groups`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, description }), }); if (!res.ok) throw new Error('Failed to create group'); const json = await res.json(); return VehicleGroupSchema.parse(json); } export async function updateGroup(id: number, name: string, description?: string): Promise { const res = await fetch(`${API_URL}/admin/groups/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, description }), }); if (!res.ok) throw new Error('Failed to update group'); const json = await res.json(); return VehicleGroupSchema.parse(json); } export async function assignVehicleToGroup(groupId: number, vin: string): Promise { const res = await fetch(`${API_URL}/admin/groups/${groupId}/vehicles`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ vin }), }); if (!res.ok) throw new Error('Failed to assign vehicle to group'); } export async function fetchDeployments(): Promise { const res = await fetch(`${API_URL}/admin/deployments`); if (!res.ok) throw new Error('Failed to fetch deployments'); const json = await res.json(); return z.array(DeploymentSchema).parse(json); } export async function uploadUpdate(formData: FormData): Promise { const res = await fetch(`${API_URL}/admin/updates`, { method: 'POST', body: formData, }); if (!res.ok) throw new Error('Failed to upload update'); } export async function createDeployment(updateId: number, targetVin?: string, targetGroupId?: number): Promise { const res = await fetch(`${API_URL}/admin/deployments`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ updateId, targetVin, targetGroupId }), }); if (!res.ok) throw new Error('Failed to create deployment'); }