400 lines
12 KiB
TypeScript
400 lines
12 KiB
TypeScript
import { useEffect } from 'react';
|
||
import { Form as RemixForm } from "@remix-run/react";
|
||
import { Input } from "~/components/ui/Input";
|
||
import { Select } from "~/components/ui/Select";
|
||
import { Button } from "~/components/ui/Button";
|
||
import { FormField } from "~/components/ui/FormField";
|
||
import { Form, FormActions, FormSection, FormGrid } from "~/components/ui/Form";
|
||
import { useFormValidation } from "~/hooks/useFormValidation";
|
||
import { vehicleSchema } from "~/lib/form-validation";
|
||
import { TRANSMISSION_TYPES, FUEL_TYPES, USE_TYPES, BODY_TYPES, MANUFACTURERS, VALIDATION } from "~/lib/constants";
|
||
import type { Vehicle } from "~/types/database";
|
||
|
||
interface EnhancedVehicleFormProps {
|
||
vehicle?: Vehicle;
|
||
customers: { id: number; name: string; phone?: string | null }[];
|
||
onCancel: () => void;
|
||
errors?: Record<string, string>;
|
||
isLoading: boolean;
|
||
onSubmit?: (data: any) => void;
|
||
}
|
||
|
||
export function EnhancedVehicleForm({
|
||
vehicle,
|
||
customers,
|
||
onCancel,
|
||
errors = {},
|
||
isLoading,
|
||
onSubmit,
|
||
}: EnhancedVehicleFormProps) {
|
||
const {
|
||
values,
|
||
errors: validationErrors,
|
||
touched,
|
||
isValid,
|
||
setValue,
|
||
setTouched,
|
||
reset,
|
||
validate,
|
||
getFieldProps,
|
||
} = useFormValidation({
|
||
schema: vehicleSchema,
|
||
initialValues: {
|
||
plateNumber: vehicle?.plateNumber || "",
|
||
bodyType: vehicle?.bodyType || "",
|
||
manufacturer: vehicle?.manufacturer || "",
|
||
model: vehicle?.model || "",
|
||
trim: vehicle?.trim || "",
|
||
year: vehicle?.year || new Date().getFullYear(),
|
||
transmission: vehicle?.transmission || "",
|
||
fuel: vehicle?.fuel || "",
|
||
cylinders: vehicle?.cylinders || null,
|
||
engineDisplacement: vehicle?.engineDisplacement || null,
|
||
useType: vehicle?.useType || "",
|
||
ownerId: vehicle?.ownerId || 0,
|
||
},
|
||
validateOnChange: true,
|
||
validateOnBlur: true,
|
||
});
|
||
|
||
// Reset form when vehicle changes
|
||
useEffect(() => {
|
||
if (vehicle) {
|
||
reset({
|
||
plateNumber: vehicle.plateNumber || "",
|
||
bodyType: vehicle.bodyType || "",
|
||
manufacturer: vehicle.manufacturer || "",
|
||
model: vehicle.model || "",
|
||
trim: vehicle.trim || "",
|
||
year: vehicle.year || new Date().getFullYear(),
|
||
transmission: vehicle.transmission || "",
|
||
fuel: vehicle.fuel || "",
|
||
cylinders: vehicle.cylinders || null,
|
||
engineDisplacement: vehicle.engineDisplacement || null,
|
||
useType: vehicle.useType || "",
|
||
ownerId: vehicle.ownerId || 0,
|
||
});
|
||
} else {
|
||
reset({
|
||
plateNumber: "",
|
||
bodyType: "",
|
||
manufacturer: "",
|
||
model: "",
|
||
trim: "",
|
||
year: new Date().getFullYear(),
|
||
transmission: "",
|
||
fuel: "",
|
||
cylinders: null,
|
||
engineDisplacement: null,
|
||
useType: "",
|
||
ownerId: 0,
|
||
});
|
||
}
|
||
}, [vehicle, reset]);
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
const { isValid: formIsValid } = validate();
|
||
|
||
if (formIsValid && onSubmit) {
|
||
onSubmit(values);
|
||
}
|
||
};
|
||
|
||
const isEditing = !!vehicle;
|
||
const combinedErrors = { ...validationErrors, ...errors };
|
||
const currentYear = new Date().getFullYear();
|
||
|
||
return (
|
||
<Form
|
||
title={isEditing ? "تعديل بيانات المركبة" : "إضافة مركبة جديدة"}
|
||
description={isEditing ? "قم بتعديل بيانات المركبة أدناه" : "أدخل بيانات المركبة الجديدة"}
|
||
loading={isLoading}
|
||
onSubmit={handleSubmit}
|
||
>
|
||
<input
|
||
type="hidden"
|
||
name="_action"
|
||
value={isEditing ? "update" : "create"}
|
||
/>
|
||
{isEditing && (
|
||
<input type="hidden" name="id" value={vehicle.id} />
|
||
)}
|
||
|
||
<FormSection
|
||
title="المعلومات الأساسية"
|
||
description="البيانات الأساسية للمركبة"
|
||
>
|
||
<FormGrid columns={2}>
|
||
{/* Plate Number */}
|
||
<FormField
|
||
label="رقم اللوحة"
|
||
required
|
||
error={combinedErrors.plateNumber}
|
||
htmlFor="plateNumber"
|
||
>
|
||
<Input
|
||
id="plateNumber"
|
||
name="plateNumber"
|
||
type="text"
|
||
placeholder="أدخل رقم اللوحة"
|
||
disabled={isLoading}
|
||
dir="ltr"
|
||
{...getFieldProps('plateNumber')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Owner */}
|
||
<FormField
|
||
label="المالك"
|
||
required
|
||
error={combinedErrors.ownerId}
|
||
htmlFor="ownerId"
|
||
>
|
||
<Select
|
||
id="ownerId"
|
||
name="ownerId"
|
||
placeholder="اختر المالك"
|
||
disabled={isLoading}
|
||
options={customers.map(customer => ({
|
||
value: customer.id.toString(),
|
||
label: `${customer.name}${customer.phone ? ` (${customer.phone})` : ''}`,
|
||
}))}
|
||
value={values.ownerId?.toString() || ''}
|
||
onChange={(e) => setValue('ownerId', parseInt(e.target.value) || 0)}
|
||
/>
|
||
</FormField>
|
||
</FormGrid>
|
||
</FormSection>
|
||
|
||
<FormSection
|
||
title="مواصفات المركبة"
|
||
description="التفاصيل التقنية للمركبة"
|
||
>
|
||
<FormGrid columns={3}>
|
||
{/* Body Type */}
|
||
<FormField
|
||
label="نوع الهيكل"
|
||
required
|
||
error={combinedErrors.bodyType}
|
||
htmlFor="bodyType"
|
||
>
|
||
<Select
|
||
id="bodyType"
|
||
name="bodyType"
|
||
placeholder="اختر نوع الهيكل"
|
||
aria-readonly={isLoading}
|
||
options={BODY_TYPES.map(type => ({
|
||
value: type.value,
|
||
label: type.label,
|
||
}))}
|
||
{...getFieldProps('bodyType')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Manufacturer */}
|
||
<FormField
|
||
label="الشركة المصنعة"
|
||
required
|
||
error={combinedErrors.manufacturer}
|
||
htmlFor="manufacturer"
|
||
>
|
||
<Select
|
||
id="manufacturer"
|
||
name="manufacturer"
|
||
placeholder="اختر الشركة المصنعة"
|
||
disabled={isLoading}
|
||
options={MANUFACTURERS.map(manufacturer => ({
|
||
value: manufacturer.value,
|
||
label: manufacturer.label,
|
||
}))}
|
||
{...getFieldProps('manufacturer')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Model */}
|
||
<FormField
|
||
label="الموديل"
|
||
required
|
||
error={combinedErrors.model}
|
||
htmlFor="model"
|
||
>
|
||
<Input
|
||
id="model"
|
||
name="model"
|
||
type="text"
|
||
placeholder="أدخل الموديل"
|
||
disabled={isLoading}
|
||
{...getFieldProps('model')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Trim */}
|
||
<FormField
|
||
label="الفئة"
|
||
error={combinedErrors.trim}
|
||
htmlFor="trim"
|
||
helperText="الفئة اختيارية"
|
||
>
|
||
<Input
|
||
id="trim"
|
||
name="trim"
|
||
type="text"
|
||
placeholder="أدخل الفئة (اختياري)"
|
||
disabled={isLoading}
|
||
{...getFieldProps('trim')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Year */}
|
||
<FormField
|
||
label="سنة الصنع"
|
||
required
|
||
error={combinedErrors.year}
|
||
htmlFor="year"
|
||
>
|
||
<Input
|
||
id="year"
|
||
name="year"
|
||
type="number"
|
||
min={VALIDATION.MIN_YEAR}
|
||
max={VALIDATION.MAX_YEAR}
|
||
placeholder={`${VALIDATION.MIN_YEAR} - ${currentYear}`}
|
||
disabled={isLoading}
|
||
value={values.year?.toString() || ''}
|
||
onChange={(e) => setValue('year', parseInt(e.target.value) || currentYear)}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Use Type */}
|
||
<FormField
|
||
label="نوع الاستخدام"
|
||
required
|
||
error={combinedErrors.useType}
|
||
htmlFor="useType"
|
||
>
|
||
<Select
|
||
id="useType"
|
||
name="useType"
|
||
placeholder="اختر نوع الاستخدام"
|
||
disabled={isLoading}
|
||
options={USE_TYPES.map(useType => ({
|
||
value: useType.value,
|
||
label: useType.label,
|
||
}))}
|
||
{...getFieldProps('useType')}
|
||
/>
|
||
</FormField>
|
||
</FormGrid>
|
||
</FormSection>
|
||
|
||
<FormSection
|
||
title="المحرك والناقل"
|
||
description="مواصفات المحرك وناقل الحركة"
|
||
>
|
||
<FormGrid columns={2}>
|
||
{/* Transmission */}
|
||
<FormField
|
||
label="ناقل الحركة"
|
||
required
|
||
error={combinedErrors.transmission}
|
||
htmlFor="transmission"
|
||
>
|
||
<Select
|
||
id="transmission"
|
||
name="transmission"
|
||
placeholder="اختر ناقل الحركة"
|
||
disabled={isLoading}
|
||
options={TRANSMISSION_TYPES.map(transmission => ({
|
||
value: transmission.value,
|
||
label: transmission.label,
|
||
}))}
|
||
{...getFieldProps('transmission')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Fuel */}
|
||
<FormField
|
||
label="نوع الوقود"
|
||
required
|
||
error={combinedErrors.fuel}
|
||
htmlFor="fuel"
|
||
>
|
||
<Select
|
||
id="fuel"
|
||
name="fuel"
|
||
placeholder="اختر نوع الوقود"
|
||
disabled={isLoading}
|
||
options={FUEL_TYPES.map(fuel => ({
|
||
value: fuel.value,
|
||
label: fuel.label,
|
||
}))}
|
||
{...getFieldProps('fuel')}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Cylinders */}
|
||
<FormField
|
||
label="عدد الأسطوانات"
|
||
error={combinedErrors.cylinders}
|
||
htmlFor="cylinders"
|
||
helperText="عدد الأسطوانات اختياري"
|
||
>
|
||
<Input
|
||
id="cylinders"
|
||
name="cylinders"
|
||
type="number"
|
||
min="1"
|
||
max={VALIDATION.MAX_CYLINDERS}
|
||
placeholder="عدد الأسطوانات (اختياري)"
|
||
disabled={isLoading}
|
||
value={values.cylinders?.toString() || ''}
|
||
onChange={(e) => setValue('cylinders', e.target.value ? parseInt(e.target.value) : null)}
|
||
/>
|
||
</FormField>
|
||
|
||
{/* Engine Displacement */}
|
||
<FormField
|
||
label="سعة المحرك (لتر)"
|
||
error={combinedErrors.engineDisplacement}
|
||
htmlFor="engineDisplacement"
|
||
helperText="سعة المحرك اختيارية"
|
||
>
|
||
<Input
|
||
id="engineDisplacement"
|
||
name="engineDisplacement"
|
||
type="number"
|
||
step="0.1"
|
||
min="0.1"
|
||
max={VALIDATION.MAX_ENGINE_DISPLACEMENT}
|
||
placeholder="سعة المحرك (اختياري)"
|
||
disabled={isLoading}
|
||
value={values.engineDisplacement?.toString() || ''}
|
||
onChange={(e) => setValue('engineDisplacement', e.target.value ? parseFloat(e.target.value) : null)}
|
||
/>
|
||
</FormField>
|
||
</FormGrid>
|
||
</FormSection>
|
||
|
||
<FormActions>
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
onClick={onCancel}
|
||
disabled={isLoading}
|
||
>
|
||
إلغاء
|
||
</Button>
|
||
|
||
<Button
|
||
type="submit"
|
||
disabled={isLoading || !isValid || !values.plateNumber?.trim() || !values.ownerId}
|
||
loading={isLoading}
|
||
>
|
||
{isEditing ? "تحديث المركبة" : "إنشاء المركبة"}
|
||
</Button>
|
||
</FormActions>
|
||
</Form>
|
||
);
|
||
} |