feat: update frontend for Gemini VIN decode (refs #228)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 6m31s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 23s
Deploy to Staging / Verify Staging (pull_request) Successful in 9s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

Rename nhtsaValue to sourceValue in frontend MatchedField type and
VinOcrReviewModal component. Update NHTSA references to vehicle
database across guide pages, hooks, and API documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-02-18 21:51:45 -06:00
parent f590421058
commit d96736789e
7 changed files with 28 additions and 28 deletions

View File

@@ -82,7 +82,7 @@ export const vehiclesApi = {
}, },
/** /**
* Decode VIN using NHTSA vPIC API * Decode VIN using VIN decode service
* Requires Pro or Enterprise tier * Requires Pro or Enterprise tier
*/ */
decodeVin: async (vin: string): Promise<DecodedVehicleData> => { decodeVin: async (vin: string): Promise<DecodedVehicleData> => {

View File

@@ -507,7 +507,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
/** /**
* Handle VIN decode button click * Handle VIN decode button click
* Calls NHTSA API and populates empty form fields * Calls VIN decode service and populates empty form fields
*/ */
const handleDecodeVin = async () => { const handleDecodeVin = async () => {
// Check tier access first // Check tier access first

View File

@@ -95,8 +95,8 @@ const ReviewContent: React.FC<{
const [selectedEngine, setSelectedEngine] = useState(''); const [selectedEngine, setSelectedEngine] = useState('');
const [selectedTransmission, setSelectedTransmission] = useState(''); const [selectedTransmission, setSelectedTransmission] = useState('');
// NHTSA reference values for unmatched fields // Source reference values for unmatched fields
const [nhtsaRefs, setNhtsaRefs] = useState<Record<string, string | null>>({}); const [sourceRefs, setSourceRefs] = useState<Record<string, string | null>>({});
// Initialize dropdown options and pre-select decoded values // Initialize dropdown options and pre-select decoded values
useEffect(() => { useEffect(() => {
@@ -109,13 +109,13 @@ const ReviewContent: React.FC<{
if (!decodedVehicle) return; if (!decodedVehicle) return;
// Store NHTSA reference values for unmatched fields // Store source reference values for unmatched fields
setNhtsaRefs({ setSourceRefs({
make: decodedVehicle.make.confidence === 'none' ? decodedVehicle.make.nhtsaValue : null, make: decodedVehicle.make.confidence === 'none' ? decodedVehicle.make.sourceValue : null,
model: decodedVehicle.model.confidence === 'none' ? decodedVehicle.model.nhtsaValue : null, model: decodedVehicle.model.confidence === 'none' ? decodedVehicle.model.sourceValue : null,
trim: decodedVehicle.trimLevel.confidence === 'none' ? decodedVehicle.trimLevel.nhtsaValue : null, trim: decodedVehicle.trimLevel.confidence === 'none' ? decodedVehicle.trimLevel.sourceValue : null,
engine: decodedVehicle.engine.confidence === 'none' ? decodedVehicle.engine.nhtsaValue : null, engine: decodedVehicle.engine.confidence === 'none' ? decodedVehicle.engine.sourceValue : null,
transmission: decodedVehicle.transmission.confidence === 'none' ? decodedVehicle.transmission.nhtsaValue : null, transmission: decodedVehicle.transmission.confidence === 'none' ? decodedVehicle.transmission.sourceValue : null,
}); });
const yearValue = decodedVehicle.year.value; const yearValue = decodedVehicle.year.value;
@@ -277,9 +277,9 @@ const ReviewContent: React.FC<{
}); });
}; };
/** Show NHTSA reference when field had no dropdown match */ /** Show source reference when field had no dropdown match */
const nhtsaHint = (field: string) => { const sourceHint = (field: string) => {
const ref = nhtsaRefs[field]; const ref = sourceRefs[field];
if (!ref) return null; if (!ref) return null;
// Only show hint when no value is currently selected // Only show hint when no value is currently selected
const selected: Record<string, string> = { const selected: Record<string, string> = {
@@ -292,7 +292,7 @@ const ReviewContent: React.FC<{
if (selected[field]) return null; if (selected[field]) return null;
return ( return (
<p className="mt-1 text-xs text-gray-500 dark:text-titanio"> <p className="mt-1 text-xs text-gray-500 dark:text-titanio">
NHTSA returned: {ref} Decoded value: {ref}
</p> </p>
); );
}; };
@@ -409,7 +409,7 @@ const ReviewContent: React.FC<{
</option> </option>
))} ))}
</select> </select>
{nhtsaHint('make')} {sourceHint('make')}
</div> </div>
{/* Model */} {/* Model */}
@@ -439,7 +439,7 @@ const ReviewContent: React.FC<{
</option> </option>
))} ))}
</select> </select>
{nhtsaHint('model')} {sourceHint('model')}
</div> </div>
{/* Trim */} {/* Trim */}
@@ -469,7 +469,7 @@ const ReviewContent: React.FC<{
</option> </option>
))} ))}
</select> </select>
{nhtsaHint('trim')} {sourceHint('trim')}
</div> </div>
{/* Engine */} {/* Engine */}
@@ -499,7 +499,7 @@ const ReviewContent: React.FC<{
</option> </option>
))} ))}
</select> </select>
{nhtsaHint('engine')} {sourceHint('engine')}
</div> </div>
{/* Transmission */} {/* Transmission */}
@@ -529,7 +529,7 @@ const ReviewContent: React.FC<{
</option> </option>
))} ))}
</select> </select>
{nhtsaHint('transmission')} {sourceHint('transmission')}
</div> </div>
</div> </div>
</Box> </Box>

View File

@@ -1,5 +1,5 @@
/** /**
* @ai-summary Hook to orchestrate VIN OCR extraction and NHTSA decode * @ai-summary Hook to orchestrate VIN OCR extraction and VIN decode
* @ai-context Handles camera capture -> OCR extraction -> VIN decode flow * @ai-context Handles camera capture -> OCR extraction -> VIN decode flow
*/ */
@@ -109,7 +109,7 @@ export function useVinOcr(): UseVinOcrReturn {
); );
} }
// Step 2: Decode VIN using NHTSA // Step 2: Decode VIN
setProcessingStep('decoding'); setProcessingStep('decoding');
let decodedVehicle: DecodedVehicleData | null = null; let decodedVehicle: DecodedVehicleData | null = null;
let decodeError: string | null = null; let decodeError: string | null = null;
@@ -121,7 +121,7 @@ export function useVinOcr(): UseVinOcrReturn {
if (err.response?.data?.error === 'TIER_REQUIRED') { if (err.response?.data?.error === 'TIER_REQUIRED') {
decodeError = 'VIN decode requires Pro or Enterprise subscription'; decodeError = 'VIN decode requires Pro or Enterprise subscription';
} else if (err.response?.data?.error === 'INVALID_VIN') { } else if (err.response?.data?.error === 'INVALID_VIN') {
decodeError = 'VIN format is not recognized by NHTSA'; decodeError = 'VIN format is not recognized';
} else { } else {
decodeError = 'Unable to decode vehicle information'; decodeError = 'Unable to decode vehicle information';
} }

View File

@@ -72,12 +72,12 @@ export type MatchConfidence = 'high' | 'medium' | 'none';
*/ */
export interface MatchedField<T> { export interface MatchedField<T> {
value: T | null; value: T | null;
nhtsaValue: string | null; sourceValue: string | null;
confidence: MatchConfidence; confidence: MatchConfidence;
} }
/** /**
* Decoded vehicle data from NHTSA vPIC API * Decoded vehicle data from VIN decode
* with match confidence per field * with match confidence per field
*/ */
export interface DecodedVehicleData { export interface DecodedVehicleData {

View File

@@ -43,7 +43,7 @@ export const SubscriptionSection = () => {
</h3> </h3>
<p className="text-titanio/70 leading-relaxed mb-4"> <p className="text-titanio/70 leading-relaxed mb-4">
<strong className="text-avus">What it does:</strong> Use your device camera to photograph your vehicle's VIN plate, and the system automatically reads the VIN using OCR (Optical Character Recognition) and decodes it from the NHTSA database. <strong className="text-avus">What it does:</strong> Use your device camera to photograph your vehicle's VIN plate, and the system automatically reads the VIN using OCR (Optical Character Recognition) and decodes it from the vehicle database.
</p> </p>
<p className="text-titanio/70 leading-relaxed mb-4"> <p className="text-titanio/70 leading-relaxed mb-4">
@@ -58,7 +58,7 @@ export const SubscriptionSection = () => {
<li>A <strong className="text-avus">VIN OCR Review modal</strong> appears showing the detected VIN with confidence indicators</li> <li>A <strong className="text-avus">VIN OCR Review modal</strong> appears showing the detected VIN with confidence indicators</li>
<li>Confirm or correct the VIN, then click <strong className="text-avus">Accept</strong></li> <li>Confirm or correct the VIN, then click <strong className="text-avus">Accept</strong></li>
<li>Click the <strong className="text-avus">Decode VIN</strong> button</li> <li>Click the <strong className="text-avus">Decode VIN</strong> button</li>
<li>The system queries the NHTSA database and auto-populates: Year, Make, Model, Engine, Transmission, and Trim</li> <li>The system queries the vehicle database and auto-populates: Year, Make, Model, Engine, Transmission, and Trim</li>
<li>Review the pre-filled fields and complete the remaining details</li> <li>Review the pre-filled fields and complete the remaining details</li>
</ol> </ol>

View File

@@ -141,7 +141,7 @@ export const VehiclesSection = () => {
<GuideScreenshot <GuideScreenshot
src="/guide/vin-decode-desktop.png" src="/guide/vin-decode-desktop.png"
alt="VIN Decode feature showing auto-populated vehicle specifications" alt="VIN Decode feature showing auto-populated vehicle specifications"
caption="The VIN Decode feature automatically fills in vehicle details from the NHTSA database" caption="The VIN Decode feature automatically fills in vehicle details from the vehicle database"
/> />
</div> </div>