Homepage Redesign

This commit is contained in:
Eric Gullickson
2025-11-03 14:06:54 -06:00
parent 54d97a98b5
commit eeb20543fa
71 changed files with 3925 additions and 1340 deletions

View File

@@ -32,40 +32,40 @@ export const vehiclesApi = {
await apiClient.delete(`/vehicles/${id}`);
},
// Dropdown API methods (authenticated)
// Dropdown API methods (authenticated) - using unified platform endpoints
getYears: async (): Promise<number[]> => {
const response = await apiClient.get('/vehicles/dropdown/years');
const response = await apiClient.get('/platform/years');
return response.data;
},
getMakes: async (year: number): Promise<DropdownOption[]> => {
const response = await apiClient.get(`/vehicles/dropdown/makes?year=${year}`);
const response = await apiClient.get(`/platform/makes?year=${year}`);
return response.data;
},
getModels: async (year: number, makeId: number): Promise<DropdownOption[]> => {
const response = await apiClient.get(`/vehicles/dropdown/models?year=${year}&make_id=${makeId}`);
const response = await apiClient.get(`/platform/models?year=${year}&make_id=${makeId}`);
return response.data;
},
getTransmissions: async (year: number, makeId: number, modelId: number): Promise<DropdownOption[]> => {
const response = await apiClient.get(`/vehicles/dropdown/transmissions?year=${year}&make_id=${makeId}&model_id=${modelId}`);
const response = await apiClient.get(`/platform/transmissions?year=${year}&make_id=${makeId}&model_id=${modelId}`);
return response.data;
},
getEngines: async (year: number, makeId: number, modelId: number, trimId: number): Promise<DropdownOption[]> => {
const response = await apiClient.get(`/vehicles/dropdown/engines?year=${year}&make_id=${makeId}&model_id=${modelId}&trim_id=${trimId}`);
const response = await apiClient.get(`/platform/engines?year=${year}&make_id=${makeId}&model_id=${modelId}&trim_id=${trimId}`);
return response.data;
},
getTrims: async (year: number, makeId: number, modelId: number): Promise<DropdownOption[]> => {
const response = await apiClient.get(`/vehicles/dropdown/trims?year=${year}&make_id=${makeId}&model_id=${modelId}`);
const response = await apiClient.get(`/platform/trims?year=${year}&make_id=${makeId}&model_id=${modelId}`);
return response.data;
},
// VIN decode method
// VIN decode method - using unified platform endpoint
decodeVIN: async (vin: string): Promise<VINDecodeResponse> => {
const response = await apiClient.post('/vehicles/decode-vin', { vin });
const response = await apiClient.get(`/platform/vehicle?vin=${vin}`);
return response.data;
},
};

View File

@@ -268,11 +268,15 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
<label className="block text-sm font-medium text-gray-700 mb-1">
VIN or License Plate <span className="text-red-500">*</span>
</label>
<div className="flex gap-2">
<p className="text-xs text-gray-600 mb-2">
Enter VIN to auto-fill vehicle details OR manually select from dropdowns below
</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"
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"
@@ -280,8 +284,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
loading={decodingVIN}
disabled={!watchedVIN || watchedVIN.length !== 17}
variant="secondary"
className="w-full sm:w-auto min-h-[44px]"
>
Decode
Decode VIN
</Button>
</div>
{decodeSuccess && (
@@ -293,14 +298,15 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
{/* Vehicle Specification Dropdowns */}
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Year
</label>
<select
{...register('year', { valueAsNumber: true })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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]"
style={{ fontSize: '16px' }}
>
<option value="">Select Year</option>
{years.map((year) => (
@@ -317,8 +323,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('make')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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 || !watchedYear}
style={{ fontSize: '16px' }}
>
<option value="">Select Make</option>
{makes.map((make) => (
@@ -335,8 +342,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('model')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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 || !watchedMake || models.length === 0}
style={{ fontSize: '16px' }}
>
<option value="">Select Model</option>
{models.map((model) => (
@@ -348,7 +356,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{/* Trim (left) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
@@ -356,8 +364,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('trimLevel')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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 || trims.length === 0}
style={{ fontSize: '16px' }}
>
<option value="">Select Trim</option>
{trims.map((trim) => (
@@ -375,8 +384,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('engine')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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 || !selectedTrim || engines.length === 0}
style={{ fontSize: '16px' }}
>
<option value="">Select Engine</option>
{engines.map((engine) => (
@@ -394,7 +404,8 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<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"
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]"
style={{ fontSize: '16px' }}
>
<option value="">Select Transmission</option>
<option value="Automatic">Automatic</option>
@@ -409,20 +420,22 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<input
{...register('nickname')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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]"
placeholder="e.g., Family Car"
style={{ fontSize: '16px' }}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Color
</label>
<input
{...register('color')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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]"
placeholder="e.g., Blue"
style={{ fontSize: '16px' }}
/>
</div>
@@ -432,8 +445,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<input
{...register('licensePlate')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
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]"
placeholder="e.g., ABC-123 (required if VIN omitted)"
style={{ fontSize: '16px' }}
/>
{errors.licensePlate && (
<p className="mt-1 text-sm text-red-600">{errors.licensePlate.message}</p>
@@ -448,8 +462,10 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
<input
{...register('odometerReading', { valueAsNumber: true })}
type="number"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
inputMode="numeric"
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]"
placeholder="e.g., 50000"
style={{ fontSize: '16px' }}
/>
</div>