car_mms/app/components/vehicles/VehicleList.tsx
2025-09-11 14:22:27 +03:00

272 lines
8.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}