feat: Add VIN decoding with NHTSA vPIC API (refs #9)

- Add NHTSA client for VIN decoding with caching and validation
- Add POST /api/vehicles/decode-vin endpoint with tier gating
- Add dropdown matching service with confidence levels
- Add decode button to VehicleForm with tier check
- Responsive layout: stacks on mobile, inline on desktop
- Only populate empty fields (preserve user input)

Backend:
- NHTSAClient with 5s timeout, VIN validation, vin_cache table
- Tier gating with 'vehicle.vinDecode' feature key (Pro+)
- Tiered matching: high (exact), medium (normalized), none

Frontend:
- Decode button with loading state and error handling
- UpgradeRequiredDialog for free tier users
- Mobile-first responsive layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-11 13:55:26 -06:00
parent 84baa755d9
commit 2aae89acbe
11 changed files with 760 additions and 9 deletions

View File

@@ -3,7 +3,7 @@
*/
import { apiClient } from '../../../core/api/client';
import { Vehicle, CreateVehicleRequest, UpdateVehicleRequest } from '../types/vehicles.types';
import { Vehicle, CreateVehicleRequest, UpdateVehicleRequest, DecodedVehicleData } from '../types/vehicles.types';
// All requests (including dropdowns) use authenticated apiClient
@@ -79,5 +79,14 @@ export const vehiclesApi = {
getImageUrl: (vehicleId: string): string => {
return `/api/vehicles/${vehicleId}/image`;
},
/**
* Decode VIN using NHTSA vPIC API
* Requires Pro or Enterprise tier
*/
decodeVin: async (vin: string): Promise<DecodedVehicleData> => {
const response = await apiClient.post('/vehicles/decode-vin', { vin });
return response.data;
}
};