Possible working ETL
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { apiClient } from '../../../core/api/client';
|
||||
import { Vehicle, CreateVehicleRequest, UpdateVehicleRequest, VINDecodeResponse } from '../types/vehicles.types';
|
||||
import { Vehicle, CreateVehicleRequest, UpdateVehicleRequest } from '../types/vehicles.types';
|
||||
|
||||
// All requests (including dropdowns) use authenticated apiClient
|
||||
|
||||
@@ -48,8 +48,8 @@ export const vehiclesApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTransmissions: async (year: number, make: string, model: string): Promise<string[]> => {
|
||||
const response = await apiClient.get(`/vehicles/dropdown/transmissions?year=${year}&make=${encodeURIComponent(make)}&model=${encodeURIComponent(model)}`);
|
||||
getTransmissions: async (year: number, make: string, model: string, trim: string): Promise<string[]> => {
|
||||
const response = await apiClient.get(`/vehicles/dropdown/transmissions?year=${year}&make=${encodeURIComponent(make)}&model=${encodeURIComponent(model)}&trim=${encodeURIComponent(trim)}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -61,11 +61,5 @@ export const vehiclesApi = {
|
||||
getTrims: async (year: number, make: string, model: string): Promise<string[]> => {
|
||||
const response = await apiClient.get(`/vehicles/dropdown/trims?year=${year}&make=${encodeURIComponent(make)}&model=${encodeURIComponent(model)}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// VIN decode method - using unified platform endpoint
|
||||
decodeVIN: async (vin: string): Promise<VINDecodeResponse> => {
|
||||
const response = await apiClient.get(`/platform/vehicle?vin=${vin}`);
|
||||
return response.data;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,8 +78,6 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
const [selectedModel, setSelectedModel] = useState<string>('');
|
||||
const [selectedTrim, setSelectedTrim] = useState<string>('');
|
||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||
const [decodingVIN, setDecodingVIN] = useState(false);
|
||||
const [decodeSuccess, setDecodeSuccess] = useState(false);
|
||||
const hasInitialized = useRef(false);
|
||||
const isInitializing = useRef(false);
|
||||
|
||||
@@ -98,38 +96,6 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
const watchedYear = watch('year');
|
||||
const watchedMake = watch('make');
|
||||
const watchedModel = watch('model');
|
||||
const watchedVIN = watch('vin');
|
||||
|
||||
// VIN decode handler
|
||||
const handleDecodeVIN = async () => {
|
||||
const vin = watchedVIN;
|
||||
if (!vin || vin.length !== 17) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDecodingVIN(true);
|
||||
setDecodeSuccess(false);
|
||||
|
||||
try {
|
||||
const result = await vehiclesApi.decodeVIN(vin);
|
||||
if (result.success) {
|
||||
// Auto-populate fields with decoded values
|
||||
if (result.year) setValue('year', result.year);
|
||||
if (result.make) setValue('make', result.make);
|
||||
if (result.model) setValue('model', result.model);
|
||||
if (result.transmission) setValue('transmission', result.transmission);
|
||||
if (result.engine) setValue('engine', result.engine);
|
||||
if (result.trimLevel) setValue('trimLevel', result.trimLevel);
|
||||
|
||||
setDecodeSuccess(true);
|
||||
setTimeout(() => setDecodeSuccess(false), 3000); // Hide success after 3 seconds
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('VIN decode failed:', error);
|
||||
} finally {
|
||||
setDecodingVIN(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Load years on component mount
|
||||
useEffect(() => {
|
||||
@@ -176,25 +142,30 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Set model and load trims + transmissions
|
||||
// Step 3: Set model and load trims (transmissions loaded after trim selected)
|
||||
setSelectedModel(initialData.model);
|
||||
const [trimsData, transmissionsData] = await Promise.all([
|
||||
vehiclesApi.getTrims(initialData.year, initialData.make, initialData.model),
|
||||
vehiclesApi.getTransmissions(initialData.year, initialData.make, initialData.model)
|
||||
]);
|
||||
const trimsData = await vehiclesApi.getTrims(initialData.year, initialData.make, initialData.model);
|
||||
setTrims(trimsData);
|
||||
setTransmissions(transmissionsData);
|
||||
|
||||
if (initialData.trimLevel) {
|
||||
// Step 4: Set trim and load engines
|
||||
// Step 4: Set trim and load engines + transmissions
|
||||
setSelectedTrim(initialData.trimLevel);
|
||||
const enginesData = await vehiclesApi.getEngines(
|
||||
initialData.year,
|
||||
initialData.make,
|
||||
initialData.model,
|
||||
initialData.trimLevel
|
||||
);
|
||||
const [enginesData, transmissionsData] = await Promise.all([
|
||||
vehiclesApi.getEngines(
|
||||
initialData.year,
|
||||
initialData.make,
|
||||
initialData.model,
|
||||
initialData.trimLevel
|
||||
),
|
||||
vehiclesApi.getTransmissions(
|
||||
initialData.year,
|
||||
initialData.make,
|
||||
initialData.model,
|
||||
initialData.trimLevel
|
||||
)
|
||||
]);
|
||||
setEngines(enginesData);
|
||||
setTransmissions(transmissionsData);
|
||||
}
|
||||
|
||||
isInitializing.current = false;
|
||||
@@ -289,68 +260,75 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
}
|
||||
}, [watchedMake, watchedYear, selectedMake, setValue]);
|
||||
|
||||
// Load trims and transmissions when model changes
|
||||
// Load trims when model changes
|
||||
useEffect(() => {
|
||||
// Skip during initialization
|
||||
if (isInitializing.current) return;
|
||||
|
||||
if (watchedModel && watchedYear && selectedMake && watchedModel !== selectedModel) {
|
||||
const loadTrimsAndTransmissions = async () => {
|
||||
const loadTrims = async () => {
|
||||
setLoadingDropdowns(true);
|
||||
try {
|
||||
const [trimsData, transmissionsData] = await Promise.all([
|
||||
vehiclesApi.getTrims(watchedYear, selectedMake, watchedModel),
|
||||
vehiclesApi.getTransmissions(watchedYear, selectedMake, watchedModel)
|
||||
]);
|
||||
const trimsData = await vehiclesApi.getTrims(watchedYear, selectedMake, watchedModel);
|
||||
setTrims(trimsData);
|
||||
setTransmissions(transmissionsData);
|
||||
setSelectedModel(watchedModel);
|
||||
|
||||
// Clear deeper selections
|
||||
// Clear deeper selections (trims, transmissions, engines)
|
||||
setSelectedTrim('');
|
||||
setTransmissions([]);
|
||||
setEngines([]);
|
||||
setValue('trimLevel', '');
|
||||
setValue('transmission', '');
|
||||
setValue('engine', '');
|
||||
} catch (error) {
|
||||
console.error('Failed to load trims and transmissions:', error);
|
||||
console.error('Failed to load trims:', error);
|
||||
setTrims([]);
|
||||
setTransmissions([]);
|
||||
} finally {
|
||||
setLoadingDropdowns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadTrimsAndTransmissions();
|
||||
loadTrims();
|
||||
}
|
||||
}, [watchedModel, watchedYear, selectedMake, selectedModel, setValue]);
|
||||
|
||||
// Load engines when trim changes
|
||||
// Load engines and transmissions when trim changes
|
||||
useEffect(() => {
|
||||
// Skip during initialization
|
||||
if (isInitializing.current) return;
|
||||
|
||||
const trimName = watch('trimLevel');
|
||||
if (trimName && watchedYear && selectedMake && selectedModel) {
|
||||
const loadEngines = async () => {
|
||||
const loadEnginesAndTransmissions = async () => {
|
||||
setLoadingDropdowns(true);
|
||||
try {
|
||||
const enginesData = await vehiclesApi.getEngines(
|
||||
watchedYear,
|
||||
selectedMake,
|
||||
selectedModel,
|
||||
trimName
|
||||
);
|
||||
const [enginesData, transmissionsData] = await Promise.all([
|
||||
vehiclesApi.getEngines(
|
||||
watchedYear,
|
||||
selectedMake,
|
||||
selectedModel,
|
||||
trimName
|
||||
),
|
||||
vehiclesApi.getTransmissions(
|
||||
watchedYear,
|
||||
selectedMake,
|
||||
selectedModel,
|
||||
trimName
|
||||
)
|
||||
]);
|
||||
setEngines(enginesData);
|
||||
setTransmissions(transmissionsData);
|
||||
setSelectedTrim(trimName);
|
||||
} catch (error) {
|
||||
console.error('Failed to load engines:', error);
|
||||
console.error('Failed to load engines and transmissions:', error);
|
||||
setEngines([]);
|
||||
setTransmissions([]);
|
||||
} finally {
|
||||
setLoadingDropdowns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadEngines();
|
||||
loadEnginesAndTransmissions();
|
||||
}
|
||||
}, [watchedYear, selectedMake, selectedModel, watch('trimLevel')]);
|
||||
|
||||
@@ -361,29 +339,14 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
VIN or License Plate <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<p className="text-xs text-gray-600 mb-2">
|
||||
Enter VIN to auto-fill vehicle details OR manually select from dropdowns below
|
||||
Enter vehicle VIN (optional)
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-2">
|
||||
<input
|
||||
{...register('vin')}
|
||||
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 text-base"
|
||||
placeholder="Enter 17-character VIN (optional if License Plate provided)"
|
||||
style={{ fontSize: '16px' }}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleDecodeVIN}
|
||||
loading={decodingVIN}
|
||||
disabled={!watchedVIN || watchedVIN.length !== 17}
|
||||
variant="secondary"
|
||||
className="w-full sm:w-auto min-h-[44px]"
|
||||
>
|
||||
Decode VIN
|
||||
</Button>
|
||||
</div>
|
||||
{decodeSuccess && (
|
||||
<p className="mt-1 text-sm text-green-600">VIN decoded successfully! Fields populated.</p>
|
||||
)}
|
||||
<input
|
||||
{...register('vin')}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 text-base"
|
||||
placeholder="Enter 17-character VIN (optional if License Plate provided)"
|
||||
style={{ fontSize: '16px' }}
|
||||
/>
|
||||
{errors.vin && (
|
||||
<p className="mt-1 text-sm text-red-600">{errors.vin.message}</p>
|
||||
)}
|
||||
@@ -549,14 +512,14 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
||||
<select
|
||||
{...register('transmission')}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
|
||||
disabled={loadingDropdowns || !watchedModel || transmissions.length === 0}
|
||||
disabled={loadingDropdowns || !selectedTrim || transmissions.length === 0}
|
||||
style={{ fontSize: '16px' }}
|
||||
>
|
||||
<option value="">
|
||||
{loadingDropdowns
|
||||
? 'Loading...'
|
||||
: !watchedModel
|
||||
? 'Select model first'
|
||||
: !selectedTrim
|
||||
? 'Select trim first'
|
||||
: transmissions.length === 0
|
||||
? 'No transmissions available'
|
||||
: 'Select Transmission'}
|
||||
|
||||
@@ -52,16 +52,3 @@ export interface UpdateVehicleRequest {
|
||||
licensePlate?: string;
|
||||
odometerReading?: number;
|
||||
}
|
||||
|
||||
export interface VINDecodeResponse {
|
||||
vin: string;
|
||||
success: boolean;
|
||||
year?: number;
|
||||
make?: string;
|
||||
model?: string;
|
||||
trimLevel?: string;
|
||||
engine?: string;
|
||||
transmission?: string;
|
||||
confidence?: number;
|
||||
error?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user