import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node"; import { json } from "@remix-run/node"; import { useLoaderData, useActionData, useNavigation, useSearchParams } from "@remix-run/react"; import { useState, useEffect } from "react"; import { requireUser } from "~/lib/auth.server"; import { useDebounce } from "~/hooks/useDebounce"; import { getVehicles, createVehicle, updateVehicle, deleteVehicle, getVehicleById } from "~/lib/vehicle-management.server"; import { getCustomersForSelect } from "~/lib/customer-management.server"; import { validateVehicle } from "~/lib/validation"; import { DashboardLayout } from "~/components/layout/DashboardLayout"; import { VehicleList } from "~/components/vehicles/VehicleList"; import { VehicleForm } from "~/components/vehicles/VehicleForm"; import { VehicleDetailsView } from "~/components/vehicles/VehicleDetailsView"; import { Modal } from "~/components/ui/Modal"; import { Button } from "~/components/ui/Button"; import { Input } from "~/components/ui/Input"; import { Flex } from "~/components/layout/Flex"; import type { VehicleWithOwner, VehicleWithRelations } from "~/types/database"; export async function loader({ request }: LoaderFunctionArgs) { const user = await requireUser(request); const url = new URL(request.url); const searchQuery = url.searchParams.get("search") || ""; const page = parseInt(url.searchParams.get("page") || "1"); const limit = parseInt(url.searchParams.get("limit") || "10"); const ownerId = url.searchParams.get("ownerId") ? parseInt(url.searchParams.get("ownerId")!) : undefined; const customerId = url.searchParams.get("customerId") ? parseInt(url.searchParams.get("customerId")!) : undefined; const plateNumber = url.searchParams.get("plateNumber") || undefined; const [vehiclesResult, customers] = await Promise.all([ getVehicles(searchQuery, page, limit, customerId || ownerId, plateNumber), getCustomersForSelect(), ]); return json({ vehicles: vehiclesResult.vehicles, total: vehiclesResult.total, totalPages: vehiclesResult.totalPages, currentPage: page, searchQuery, ownerId: customerId || ownerId, customerId, plateNumber, customers, user, }); } export async function action({ request }: ActionFunctionArgs) { const user = await requireUser(request); const formData = await request.formData(); const action = formData.get("_action") as string; switch (action) { case "create": { const vehicleData = { plateNumber: formData.get("plateNumber") as string, bodyType: formData.get("bodyType") as string, manufacturer: formData.get("manufacturer") as string, model: formData.get("model") as string, trim: formData.get("trim") as string || undefined, year: parseInt(formData.get("year") as string), transmission: formData.get("transmission") as string, fuel: formData.get("fuel") as string, cylinders: formData.get("cylinders") ? parseInt(formData.get("cylinders") as string) : undefined, engineDisplacement: formData.get("engineDisplacement") ? parseFloat(formData.get("engineDisplacement") as string) : undefined, useType: formData.get("useType") as string, ownerId: parseInt(formData.get("ownerId") as string), }; // Validate vehicle data const validation = validateVehicle(vehicleData); if (!validation.isValid) { return json({ success: false, errors: validation.errors, action: "create" }, { status: 400 }); } const result = await createVehicle(vehicleData); if (result.success) { return json({ success: true, vehicle: result.vehicle, action: "create", message: "تم إنشاء المركبة بنجاح" }); } else { return json({ success: false, error: result.error, action: "create" }, { status: 400 }); } } case "update": { const id = parseInt(formData.get("id") as string); const vehicleData = { plateNumber: formData.get("plateNumber") as string, bodyType: formData.get("bodyType") as string, manufacturer: formData.get("manufacturer") as string, model: formData.get("model") as string, trim: formData.get("trim") as string || undefined, year: parseInt(formData.get("year") as string), transmission: formData.get("transmission") as string, fuel: formData.get("fuel") as string, cylinders: formData.get("cylinders") ? parseInt(formData.get("cylinders") as string) : undefined, engineDisplacement: formData.get("engineDisplacement") ? parseFloat(formData.get("engineDisplacement") as string) : undefined, useType: formData.get("useType") as string, ownerId: parseInt(formData.get("ownerId") as string), }; // Validate vehicle data const validation = validateVehicle(vehicleData); if (!validation.isValid) { return json({ success: false, errors: validation.errors, action: "update" }, { status: 400 }); } const result = await updateVehicle(id, vehicleData); if (result.success) { return json({ success: true, vehicle: result.vehicle, action: "update", message: "تم تحديث المركبة بنجاح" }); } else { return json({ success: false, error: result.error, action: "update" }, { status: 400 }); } } case "delete": { const id = parseInt(formData.get("id") as string); const result = await deleteVehicle(id); if (result.success) { return json({ success: true, action: "delete", message: "تم حذف المركبة بنجاح" }); } else { return json({ success: false, error: result.error, action: "delete" }, { status: 400 }); } } case "get": { const id = parseInt(formData.get("id") as string); const vehicle = await getVehicleById(id); if (vehicle) { return json({ success: true, vehicle, action: "get" }); } else { return json({ success: false, error: "المركبة غير موجودة", action: "get" }, { status: 404 }); } } default: return json({ success: false, error: "إجراء غير صحيح", action: "unknown" }, { status: 400 }); } } export default function VehiclesPage() { const { vehicles, total, totalPages, currentPage, searchQuery, ownerId, customerId, plateNumber, customers, user } = useLoaderData(); const actionData = useActionData(); const navigation = useNavigation(); const [searchParams, setSearchParams] = useSearchParams(); const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showViewModal, setShowViewModal] = useState(false); const [selectedVehicle, setSelectedVehicle] = useState(null); const [searchValue, setSearchValue] = useState(searchQuery); const [selectedOwnerId, setSelectedOwnerId] = useState(ownerId?.toString() || ""); const [isLoadingVehicleDetails, setIsLoadingVehicleDetails] = useState(false); const isLoading = navigation.state !== "idle"; // Debounce search value to avoid too many requests const debouncedSearchValue = useDebounce(searchValue, 300); const debouncedOwnerId = useDebounce(selectedOwnerId, 300); // Handle search automatically when debounced values change useEffect(() => { if (debouncedSearchValue !== searchQuery || debouncedOwnerId !== (ownerId?.toString() || "")) { const newSearchParams = new URLSearchParams(searchParams); if (debouncedSearchValue) { newSearchParams.set("search", debouncedSearchValue); } else { newSearchParams.delete("search"); } if (debouncedOwnerId) { newSearchParams.set("ownerId", debouncedOwnerId); } else { newSearchParams.delete("ownerId"); } newSearchParams.set("page", "1"); // Reset to first page setSearchParams(newSearchParams); } }, [debouncedSearchValue, debouncedOwnerId, searchQuery, ownerId, searchParams, setSearchParams]); // Clear search function const clearSearch = () => { setSearchValue(""); setSelectedOwnerId(""); }; // Handle pagination const handlePageChange = (page: number) => { const newSearchParams = new URLSearchParams(searchParams); newSearchParams.set("page", page.toString()); setSearchParams(newSearchParams); }; // Handle create vehicle const handleCreateVehicle = () => { setSelectedVehicle(null); setShowCreateModal(true); }; // Handle edit vehicle const handleEditVehicle = (vehicle: VehicleWithOwner | VehicleWithRelations) => { setSelectedVehicle(vehicle); setShowEditModal(true); }; // Handle view vehicle const handleViewVehicle = async (vehicle: VehicleWithOwner) => { // First show the modal with basic data setSelectedVehicle(vehicle); setShowViewModal(true); setIsLoadingVehicleDetails(true); // Then fetch full vehicle details with maintenance visits in the background try { const form = new FormData(); form.append("_action", "get"); form.append("id", vehicle.id.toString()); const response = await fetch(window.location.pathname, { method: "POST", body: form, }); if (response.ok) { const result = await response.json(); if (result.success && result.vehicle) { setSelectedVehicle(result.vehicle); } } } catch (error) { console.error("Failed to fetch full vehicle details:", error); // Keep the basic vehicle data if fetch fails } finally { setIsLoadingVehicleDetails(false); } }; // Close modals on successful action useEffect(() => { if (actionData?.success && actionData.action === "create") { setShowCreateModal(false); } if (actionData?.success && actionData.action === "update") { setShowEditModal(false); } }, [actionData]); return (
{/* Header */}

إدارة المركبات

إجمالي المركبات: {total}

{customerId && ( مفلترة حسب العميل )} {plateNumber && ( مفلترة حسب رقم اللوحة: {plateNumber} )}
{/* Search and Filters */}
setSearchValue(e.target.value)} startIcon={ } endIcon={ searchValue && ( ) } />
{(searchQuery || ownerId || debouncedSearchValue !== searchQuery || debouncedOwnerId !== (ownerId?.toString() || "")) && (
{(debouncedSearchValue !== searchQuery || debouncedOwnerId !== (ownerId?.toString() || "")) && (
جاري البحث...
)} {(searchQuery || ownerId) && ( )}
)}
{/* Action Messages */} {actionData?.success && actionData.message && (
{actionData.message}
)} {actionData?.error && (
{actionData.error}
)} {/* Vehicle List */} {/* Create Vehicle Modal */} setShowCreateModal(false)} title="إضافة مركبة جديدة" size="lg" > setShowCreateModal(false)} errors={actionData?.action === "create" ? actionData.errors : undefined} isLoading={isLoading} /> {/* Edit Vehicle Modal */} setShowEditModal(false)} title="تعديل المركبة" size="lg" > {selectedVehicle && ( setShowEditModal(false)} errors={actionData?.action === "update" ? actionData.errors : undefined} isLoading={isLoading} /> )} {/* View Vehicle Modal */} { setShowViewModal(false); setIsLoadingVehicleDetails(false); }} title={selectedVehicle ? `تفاصيل المركبة - ${selectedVehicle.plateNumber}` : "تفاصيل المركبة"} size="xl" > {selectedVehicle && ( { setShowViewModal(false); handleEditVehicle(selectedVehicle); }} onClose={() => { setShowViewModal(false); setIsLoadingVehicleDetails(false); }} isLoadingVisits={isLoadingVehicleDetails} /> )}
); }