uup
This commit is contained in:
272
app/components/vehicles/VehicleList.tsx
Normal file
272
app/components/vehicles/VehicleList.tsx
Normal file
@@ -0,0 +1,272 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user