fix: Prevent cascade clearing of VIN decoded form values (refs #9)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 2m40s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 38s
Deploy to Staging / Verify Staging (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 2m40s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 38s
Deploy to Staging / Verify Staging (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
VIN decode was setting year/make/model/trim values, but the cascading dropdown useEffects would immediately clear dependent fields because they detected a value change. Added isVinDecoding ref flag (mirroring the existing isInitializing pattern for edit mode) to skip cascade clearing during VIN decode and properly load dropdown options. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const hasInitialized = useRef(false);
|
const hasInitialized = useRef(false);
|
||||||
const isInitializing = useRef(false);
|
const isInitializing = useRef(false);
|
||||||
|
const isVinDecoding = useRef(false);
|
||||||
// Track previous values for cascade change detection (replaces useState)
|
// Track previous values for cascade change detection (replaces useState)
|
||||||
const prevYear = useRef<number | undefined>(undefined);
|
const prevYear = useRef<number | undefined>(undefined);
|
||||||
const prevMake = useRef<string>('');
|
const prevMake = useRef<string>('');
|
||||||
@@ -224,8 +225,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
// Load makes when year changes
|
// Load makes when year changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization or VIN decoding
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current || isVinDecoding.current) return;
|
||||||
|
|
||||||
if (watchedYear && watchedYear !== prevYear.current) {
|
if (watchedYear && watchedYear !== prevYear.current) {
|
||||||
const loadMakes = async () => {
|
const loadMakes = async () => {
|
||||||
@@ -262,8 +263,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
// Load models when make changes
|
// Load models when make changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization or VIN decoding
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current || isVinDecoding.current) return;
|
||||||
|
|
||||||
if (watchedMake && watchedYear && watchedMake !== prevMake.current) {
|
if (watchedMake && watchedYear && watchedMake !== prevMake.current) {
|
||||||
const loadModels = async () => {
|
const loadModels = async () => {
|
||||||
@@ -297,8 +298,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
// Load trims when model changes
|
// Load trims when model changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization or VIN decoding
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current || isVinDecoding.current) return;
|
||||||
|
|
||||||
if (watchedModel && watchedYear && watchedMake && watchedModel !== prevModel.current) {
|
if (watchedModel && watchedYear && watchedMake && watchedModel !== prevModel.current) {
|
||||||
const loadTrims = async () => {
|
const loadTrims = async () => {
|
||||||
@@ -329,8 +330,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
// Load engines and transmissions when trim changes
|
// Load engines and transmissions when trim changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization or VIN decoding
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current || isVinDecoding.current) return;
|
||||||
|
|
||||||
if (watchedTrim && watchedYear && watchedMake && watchedModel && watchedTrim !== prevTrim.current) {
|
if (watchedTrim && watchedYear && watchedMake && watchedModel && watchedTrim !== prevTrim.current) {
|
||||||
const loadEnginesAndTransmissions = async () => {
|
const loadEnginesAndTransmissions = async () => {
|
||||||
@@ -443,7 +444,17 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
try {
|
try {
|
||||||
const decoded = await vehiclesApi.decodeVin(vin);
|
const decoded = await vehiclesApi.decodeVin(vin);
|
||||||
|
|
||||||
// Only populate empty fields (preserve existing user input)
|
// Prevent cascade useEffects from clearing values during VIN decode
|
||||||
|
isVinDecoding.current = true;
|
||||||
|
setLoadingDropdowns(true);
|
||||||
|
|
||||||
|
// Track which values we're setting (only populate empty fields)
|
||||||
|
const yearToSet = !watchedYear && decoded.year.value ? decoded.year.value : watchedYear;
|
||||||
|
const makeToSet = !watchedMake && decoded.make.value ? decoded.make.value : watchedMake;
|
||||||
|
const modelToSet = !watchedModel && decoded.model.value ? decoded.model.value : watchedModel;
|
||||||
|
const trimToSet = !watchedTrim && decoded.trimLevel.value ? decoded.trimLevel.value : watchedTrim;
|
||||||
|
|
||||||
|
// Set form values
|
||||||
if (!watchedYear && decoded.year.value) {
|
if (!watchedYear && decoded.year.value) {
|
||||||
setValue('year', decoded.year.value);
|
setValue('year', decoded.year.value);
|
||||||
}
|
}
|
||||||
@@ -463,16 +474,38 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
setValue('transmission', decoded.transmission.value);
|
setValue('transmission', decoded.transmission.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body type, drive type, fuel type - check if fields are empty and we have values
|
// Load dropdown options hierarchically (like edit mode initialization)
|
||||||
const currentDriveType = watch('driveType');
|
// This ensures dropdowns have the right options for the decoded values
|
||||||
const currentFuelType = watch('fuelType');
|
if (yearToSet) {
|
||||||
|
prevYear.current = yearToSet;
|
||||||
|
const makesData = await vehiclesApi.getMakes(yearToSet);
|
||||||
|
setMakes(makesData);
|
||||||
|
|
||||||
if (!currentDriveType && decoded.driveType.nhtsaValue) {
|
if (makeToSet) {
|
||||||
// For now just show hint - user can select from dropdown
|
prevMake.current = makeToSet;
|
||||||
}
|
const modelsData = await vehiclesApi.getModels(yearToSet, makeToSet);
|
||||||
if (!currentFuelType && decoded.fuelType.nhtsaValue) {
|
setModels(modelsData);
|
||||||
// For now just show hint - user can select from dropdown
|
|
||||||
|
if (modelToSet) {
|
||||||
|
prevModel.current = modelToSet;
|
||||||
|
const trimsData = await vehiclesApi.getTrims(yearToSet, makeToSet, modelToSet);
|
||||||
|
setTrims(trimsData);
|
||||||
|
|
||||||
|
if (trimToSet) {
|
||||||
|
prevTrim.current = trimToSet;
|
||||||
|
const [enginesData, transmissionsData] = await Promise.all([
|
||||||
|
vehiclesApi.getEngines(yearToSet, makeToSet, modelToSet, trimToSet),
|
||||||
|
vehiclesApi.getTransmissions(yearToSet, makeToSet, modelToSet, trimToSet)
|
||||||
|
]);
|
||||||
|
setEngines(enginesData);
|
||||||
|
setTransmissions(transmissionsData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoadingDropdowns(false);
|
||||||
|
isVinDecoding.current = false;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('VIN decode failed:', error);
|
console.error('VIN decode failed:', error);
|
||||||
if (error.response?.data?.error === 'TIER_REQUIRED') {
|
if (error.response?.data?.error === 'TIER_REQUIRED') {
|
||||||
@@ -484,6 +517,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsDecoding(false);
|
setIsDecoding(false);
|
||||||
|
setLoadingDropdowns(false);
|
||||||
|
isVinDecoding.current = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user