Photos for vehicles

This commit is contained in:
Eric Gullickson
2025-12-15 21:39:51 -06:00
parent e1c48b7a26
commit 263fc434b0
17 changed files with 745 additions and 58 deletions

View File

@@ -7,8 +7,9 @@ import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '../../../shared-minimal/components/Button';
import { CreateVehicleRequest } from '../types/vehicles.types';
import { CreateVehicleRequest, Vehicle } from '../types/vehicles.types';
import { vehiclesApi } from '../api/vehicles.api';
import { VehicleImageUpload } from './VehicleImageUpload';
const vehicleSchema = z
.object({
@@ -57,8 +58,9 @@ const vehicleSchema = z
interface VehicleFormProps {
onSubmit: (data: CreateVehicleRequest) => void;
onCancel: () => void;
initialData?: Partial<CreateVehicleRequest>;
initialData?: Partial<CreateVehicleRequest> & { id?: string; imageUrl?: string };
loading?: boolean;
onImageUpdate?: (vehicle: Vehicle) => void;
}
export const VehicleForm: React.FC<VehicleFormProps> = ({
@@ -66,6 +68,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
onCancel,
initialData,
loading,
onImageUpdate,
}) => {
const [years, setYears] = useState<number[]>([]);
const [makes, setMakes] = useState<string[]>([]);
@@ -80,6 +83,10 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
const hasInitialized = useRef(false);
const isInitializing = useRef(false);
const [currentImageUrl, setCurrentImageUrl] = useState<string | undefined>(initialData?.imageUrl);
const isEditMode = !!initialData?.id;
const vehicleId = initialData?.id;
const {
register,
@@ -332,8 +339,53 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
}
}, [watchedYear, selectedMake, selectedModel, watch('trimLevel')]);
const handleImageUpload = async (file: File) => {
if (!vehicleId) return;
const updated = await vehiclesApi.uploadImage(vehicleId, file);
setCurrentImageUrl(updated.imageUrl);
onImageUpdate?.(updated);
};
const handleImageRemove = async () => {
if (!vehicleId) return;
await vehiclesApi.deleteImage(vehicleId);
setCurrentImageUrl(undefined);
if (initialData) {
onImageUpdate?.({ ...initialData, imageUrl: undefined } as Vehicle);
}
};
const vehicleForImage: Vehicle = {
id: vehicleId || '',
userId: '',
vin: initialData?.vin || '',
make: initialData?.make,
model: initialData?.model,
year: initialData?.year,
color: initialData?.color,
odometerReading: initialData?.odometerReading || 0,
isActive: true,
createdAt: '',
updatedAt: '',
imageUrl: currentImageUrl,
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{isEditMode && (
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
Vehicle Photo
</label>
<VehicleImageUpload
vehicle={vehicleForImage}
onUpload={handleImageUpload}
onRemove={handleImageRemove}
disabled={loading || loadingDropdowns}
/>
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
VIN or License Plate <span className="text-red-500">*</span>