diff --git a/backend/src/features/vehicles/api/vehicles.controller.ts b/backend/src/features/vehicles/api/vehicles.controller.ts index 7c7dff6..1b1297f 100644 --- a/backend/src/features/vehicles/api/vehicles.controller.ts +++ b/backend/src/features/vehicles/api/vehicles.controller.ts @@ -39,16 +39,22 @@ export class VehiclesController { async createVehicle(request: FastifyRequest<{ Body: CreateVehicleBody }>, reply: FastifyReply) { try { - // Require either a valid 17-char VIN or a non-empty license plate - const vin = request.body?.vin?.trim(); - const plate = request.body?.licensePlate?.trim(); - const hasValidVin = !!vin && vin.length === 17; - const hasPlate = !!plate && plate.length > 0; - if (!hasValidVin && !hasPlate) { - return reply.code(400).send({ - error: 'Bad Request', - message: 'Either a valid 17-character VIN or a license plate is required' - }); + // Pre-1981 vehicles have no VIN/plate requirement + const year = request.body?.year; + const isPreModern = year && year < 1981; + + if (!isPreModern) { + // Require either a valid 17-char VIN or a non-empty license plate + const vin = request.body?.vin?.trim(); + const plate = request.body?.licensePlate?.trim(); + const hasValidVin = !!vin && vin.length === 17; + const hasPlate = !!plate && plate.length > 0; + if (!hasValidVin && !hasPlate) { + return reply.code(400).send({ + error: 'Bad Request', + message: 'Either a valid 17-character VIN or a license plate is required' + }); + } } const userId = (request as any).user.sub; diff --git a/backend/src/features/vehicles/domain/vehicles.service.ts b/backend/src/features/vehicles/domain/vehicles.service.ts index d009470..83a8be7 100644 --- a/backend/src/features/vehicles/domain/vehicles.service.ts +++ b/backend/src/features/vehicles/domain/vehicles.service.ts @@ -29,11 +29,14 @@ export class VehiclesService { } async createVehicle(data: CreateVehicleRequest, userId: string): Promise { - logger.info('Creating vehicle', { userId, vin: data.vin, licensePlate: (data as any).licensePlate }); + logger.info('Creating vehicle', { userId, vin: data.vin, licensePlate: data.licensePlate }); + + // Pre-1981 vehicles have no VIN format requirement + const isPreModern = data.year && data.year < 1981; if (data.vin) { - // Validate VIN if provided - if (!isValidVIN(data.vin)) { + // Validate VIN format only for modern vehicles (1981+) + if (!isPreModern && !isValidVIN(data.vin)) { throw new Error('Invalid VIN format'); } // Duplicate check only when VIN is present diff --git a/backend/src/features/vehicles/domain/vehicles.types.ts b/backend/src/features/vehicles/domain/vehicles.types.ts index 345d12d..1ac0a5d 100644 --- a/backend/src/features/vehicles/domain/vehicles.types.ts +++ b/backend/src/features/vehicles/domain/vehicles.types.ts @@ -32,6 +32,7 @@ export interface Vehicle { export interface CreateVehicleRequest { vin?: string; + year?: number; make?: string; model?: string; engine?: string; @@ -101,6 +102,14 @@ export interface VINDecodeResult { // Fastify-specific types for HTTP handling export interface CreateVehicleBody { vin?: string; + year?: number; + make?: string; + model?: string; + engine?: string; + transmission?: string; + trimLevel?: string; + driveType?: string; + fuelType?: string; nickname?: string; color?: string; licensePlate?: string; diff --git a/docs/PROMPTS.md b/docs/PROMPTS.md index 45ac502..a6d362b 100644 --- a/docs/PROMPTS.md +++ b/docs/PROMPTS.md @@ -30,14 +30,9 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en *** CHANGES TO IMPLEMENT *** - Research this code base and ask iterative questions to compile a complete plan. - We will pair troubleshoot this. Tell me what logs and things to run and I will -- The CSV import for the vehicle catalog fails with a basic file -- Here is the data trying to import -year,make,model,trim,engine_name,transmission_type - 1968,Chevrolet,Camaro,Rally Sport Coupe,V-8,Manual - 1969,Oldsmobile,Cutlass,F85,V-8,Automatic -- Here is the error. It appears it's failing the insert because there are duplicate V-8 engine items. This shouldn't cause a failure. This should be handled gracefully. -Row 0: Failed to upsert 1968 Chevrolet Camaro Rally Sport Coupe: duplicate key value violates unique constraint "engines_pkey" -Row 0: Failed to upsert 1969 Oldsmobile Cutlass F85: current transaction is aborted, commands ignored until end of transaction block +- There is a bug in the ability to add and edit vehicles +- Change it so the every vehicle older than 1981 does not have a 17 digit VIN requirement. +- Verify the edit logic. When I had a license plate but no VIN, it still said the VIN was required. diff --git a/frontend/src/features/vehicles/components/VehicleForm.tsx b/frontend/src/features/vehicles/components/VehicleForm.tsx index 5661133..9be6441 100644 --- a/frontend/src/features/vehicles/components/VehicleForm.tsx +++ b/frontend/src/features/vehicles/components/VehicleForm.tsx @@ -13,22 +13,25 @@ import { VehicleImageUpload } from './VehicleImageUpload'; const vehicleSchema = z .object({ - vin: z.string().optional(), - year: z.number().min(1950).max(new Date().getFullYear() + 1).optional(), - make: z.string().optional(), - model: z.string().optional(), - engine: z.string().optional(), - transmission: z.string().optional(), - trimLevel: z.string().optional(), - driveType: z.string().optional(), - fuelType: z.string().optional(), - nickname: z.string().optional(), - color: z.string().optional(), - licensePlate: z.string().optional(), - odometerReading: z.number().min(0).optional(), + vin: z.string().nullable().optional().transform(val => val ?? undefined), + year: z.number().min(1950).max(new Date().getFullYear() + 1).nullable().optional(), + make: z.string().nullable().optional(), + model: z.string().nullable().optional(), + engine: z.string().nullable().optional(), + transmission: z.string().nullable().optional(), + trimLevel: z.string().nullable().optional(), + driveType: z.string().nullable().optional(), + fuelType: z.string().nullable().optional(), + nickname: z.string().nullable().optional(), + color: z.string().nullable().optional(), + licensePlate: z.string().nullable().optional(), + odometerReading: z.number().min(0).nullable().optional(), }) .refine( (data) => { + // Pre-1981 vehicles have no VIN/plate requirement + if (data.year && data.year < 1981) return true; + const vin = (data.vin || '').trim(); const plate = (data.licensePlate || '').trim(); // Must have either a valid 17-char VIN or a non-empty license plate @@ -43,6 +46,9 @@ const vehicleSchema = z ) .refine( (data) => { + // Pre-1981 vehicles have no VIN format requirement + if (data.year && data.year < 1981) return true; + const vin = (data.vin || '').trim(); const plate = (data.licensePlate || '').trim(); // If VIN provided but not 17 and no plate, fail; if plate exists, allow any VIN (or empty)