272 lines
8.1 KiB
TypeScript
272 lines
8.1 KiB
TypeScript
import { Form, Link } from "@remix-run/react";
|
||
import { useState, useEffect } from "react";
|
||
import { DataTable, Pagination } from "~/components/ui/DataTable";
|
||
import { Button } from "~/components/ui/Button";
|
||
import { Flex } from "~/components/layout/Flex";
|
||
import { useSettings } from "~/contexts/SettingsContext";
|
||
import { TRANSMISSION_TYPES, FUEL_TYPES, USE_TYPES, BODY_TYPES } from "~/lib/constants";
|
||
import type { VehicleWithOwner } from "~/types/database";
|
||
|
||
interface VehicleListProps {
|
||
vehicles: VehicleWithOwner[];
|
||
currentPage: number;
|
||
totalPages: number;
|
||
onPageChange: (page: number) => void;
|
||
onEditVehicle: (vehicle: VehicleWithOwner) => void;
|
||
onViewVehicle?: (vehicle: VehicleWithOwner) => void;
|
||
isLoading: boolean;
|
||
actionData?: any;
|
||
}
|
||
|
||
export function VehicleList({
|
||
vehicles,
|
||
currentPage,
|
||
totalPages,
|
||
onPageChange,
|
||
onEditVehicle,
|
||
onViewVehicle,
|
||
isLoading,
|
||
actionData,
|
||
}: VehicleListProps) {
|
||
const { formatDate } = useSettings();
|
||
const [deletingVehicleId, setDeletingVehicleId] = useState<number | null>(null);
|
||
|
||
// Reset deleting state when delete action completes
|
||
useEffect(() => {
|
||
if (actionData?.success && actionData.action === "delete") {
|
||
setDeletingVehicleId(null);
|
||
}
|
||
}, [actionData]);
|
||
|
||
// Helper functions to get display labels
|
||
const getTransmissionLabel = (value: string) => {
|
||
return TRANSMISSION_TYPES.find(t => t.value === value)?.label || value;
|
||
};
|
||
|
||
const getFuelLabel = (value: string) => {
|
||
return FUEL_TYPES.find(f => f.value === value)?.label || value;
|
||
};
|
||
|
||
const getUseTypeLabel = (value: string) => {
|
||
return USE_TYPES.find(u => u.value === value)?.label || value;
|
||
};
|
||
|
||
const getBodyTypeLabel = (value: string) => {
|
||
return BODY_TYPES.find(b => b.value === value)?.label || value;
|
||
};
|
||
|
||
const columns = [
|
||
{
|
||
key: "plateNumber",
|
||
header: "رقم اللوحة",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div>
|
||
<Link
|
||
to={`/vehicles/${vehicle.id}`}
|
||
className="font-mono text-lg font-medium text-blue-600 hover:text-blue-800"
|
||
>
|
||
{vehicle.plateNumber}
|
||
</Link>
|
||
<div className="text-sm text-gray-500">
|
||
المركبة رقم: {vehicle.id}
|
||
</div>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "vehicle",
|
||
header: "تفاصيل المركبة",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div>
|
||
<div className="font-medium text-gray-900">
|
||
{vehicle.manufacturer} {vehicle.model}
|
||
</div>
|
||
<div className="text-sm text-gray-600">
|
||
{vehicle.year} • {getBodyTypeLabel(vehicle.bodyType)}
|
||
</div>
|
||
{vehicle.trim && (
|
||
<div className="text-sm text-gray-500">
|
||
فئة: {vehicle.trim}
|
||
</div>
|
||
)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "specifications",
|
||
header: "المواصفات",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div className="space-y-1">
|
||
<div className="text-sm text-gray-900">
|
||
{getTransmissionLabel(vehicle.transmission)}
|
||
</div>
|
||
<div className="text-sm text-gray-600">
|
||
{getFuelLabel(vehicle.fuel)}
|
||
</div>
|
||
{vehicle.cylinders && (
|
||
<div className="text-sm text-gray-500">
|
||
{vehicle.cylinders} أسطوانة
|
||
</div>
|
||
)}
|
||
{vehicle.engineDisplacement && (
|
||
<div className="text-sm text-gray-500">
|
||
{vehicle.engineDisplacement}L
|
||
</div>
|
||
)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "owner",
|
||
header: "المالك",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div>
|
||
<Link
|
||
to={`/customers/${vehicle.owner.id}`}
|
||
className="font-medium text-blue-600 hover:text-blue-800"
|
||
>
|
||
{vehicle.owner.name}
|
||
</Link>
|
||
{vehicle.owner.phone && (
|
||
<div className="text-sm text-gray-500" dir="ltr">
|
||
{vehicle.owner.phone}
|
||
</div>
|
||
)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "useType",
|
||
header: "نوع الاستخدام",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div className="text-sm text-gray-900">
|
||
{getUseTypeLabel(vehicle.useType)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "maintenance",
|
||
header: "الصيانة",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div className="space-y-1">
|
||
{vehicle.lastVisitDate ? (
|
||
<div className="text-sm text-gray-900">
|
||
آخر زيارة: {formatDate(vehicle.lastVisitDate)}
|
||
</div>
|
||
) : (
|
||
<div className="text-sm text-gray-400">
|
||
لا توجد زيارات
|
||
</div>
|
||
)}
|
||
{vehicle.suggestedNextVisitDate && (
|
||
<div className="text-sm text-orange-600">
|
||
الزيارة التالية: {formatDate(vehicle.suggestedNextVisitDate)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "createdDate",
|
||
header: "تاريخ التسجيل",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<div className="text-sm text-gray-600">
|
||
{formatDate(vehicle.createdDate)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "actions",
|
||
header: "الإجراءات",
|
||
render: (vehicle: VehicleWithOwner) => (
|
||
<Flex className="flex-wrap gap-2">
|
||
{onViewVehicle ? (
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
onClick={() => onViewVehicle(vehicle)}
|
||
disabled={isLoading}
|
||
>
|
||
عرض
|
||
</Button>
|
||
) : (
|
||
<Link to={`/vehicles/${vehicle.id}`}>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
disabled={isLoading}
|
||
>
|
||
عرض
|
||
</Button>
|
||
</Link>
|
||
)}
|
||
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
onClick={() => onEditVehicle(vehicle)}
|
||
disabled={isLoading}
|
||
>
|
||
تعديل
|
||
</Button>
|
||
|
||
<Form method="post" className="inline">
|
||
<input type="hidden" name="_action" value="delete" />
|
||
<input type="hidden" name="id" value={vehicle.id} />
|
||
<Button
|
||
type="submit"
|
||
size="sm"
|
||
variant="outline"
|
||
className="text-red-600 border-red-300 hover:bg-red-50"
|
||
disabled={isLoading || deletingVehicleId === vehicle.id}
|
||
onClick={(e) => {
|
||
e.preventDefault();
|
||
if (window.confirm("هل أنت متأكد من حذف هذه المركبة؟")) {
|
||
setDeletingVehicleId(vehicle.id);
|
||
(e.target as HTMLButtonElement).form?.submit();
|
||
}
|
||
}}
|
||
>
|
||
{deletingVehicleId === vehicle.id ? "جاري الحذف..." : "حذف"}
|
||
</Button>
|
||
</Form>
|
||
</Flex>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<div className="bg-white rounded-lg shadow-sm border">
|
||
{vehicles.length === 0 ? (
|
||
<div className="text-center py-12">
|
||
<div className="text-gray-400 text-lg mb-2">🚗</div>
|
||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||
لا توجد مركبات
|
||
</h3>
|
||
<p className="text-gray-500">
|
||
لم يتم العثور على أي مركبات. قم بإضافة مركبة جديدة للبدء.
|
||
</p>
|
||
</div>
|
||
) : (
|
||
<DataTable
|
||
data={vehicles}
|
||
columns={columns}
|
||
loading={isLoading}
|
||
emptyMessage="لم يتم العثور على أي مركبات"
|
||
/>
|
||
)}
|
||
</div>
|
||
|
||
{totalPages > 1 && (
|
||
<div className="flex justify-center">
|
||
<Pagination
|
||
currentPage={currentPage}
|
||
totalPages={totalPages}
|
||
onPageChange={onPageChange}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
} |