diff --git a/app/components/layout/ConditionalNavbar.tsx b/app/components/layout/ConditionalNavbar.tsx index e23fa77..ab7de6e 100644 --- a/app/components/layout/ConditionalNavbar.tsx +++ b/app/components/layout/ConditionalNavbar.tsx @@ -1,12 +1,21 @@ +// app/components/layout/ConditionalNavbar.tsx "use client"; import { usePathname } from "next/navigation"; -import { getStoredUser } from "@/src/lib/auth"; +import { useStoredUser } from "@/src/hooks/useStoredUser"; import Navbar from "./Navbar"; export default function ConditionalNavbar() { const pathname = usePathname(); - const user = getStoredUser(); + + // استبدلنا getStoredUser() المباشر بالـ hook عشان يبقى SSR-safe + const { user, loading } = useStoredUser(); + + // لحد ما نتأكد من حالة تسجيل الدخول من الـ storage، منوريش/مخفيش + // الناف بار عشان نتجنب وميض (flash) قبل الـ hydration يخلص. مفيش + // حاجة تتعرض هنا أصلاً (مفيش avatar/اسم في الناف بار العام)، فـ null + // كافية ومفيش داعي لـ Spinner/InlineLoader في المكان ده. + if (loading) return null; // لو المستخدم مسجل دخول، أو الصفحة الحالية جوه الداشبورد، منظهرش الناف بار العام const isDashboardRoute = pathname?.startsWith("/dashboard"); diff --git a/app/components/layout/Sidebar.tsx b/app/components/layout/Sidebar.tsx index 9c2013f..d498dc0 100644 --- a/app/components/layout/Sidebar.tsx +++ b/app/components/layout/Sidebar.tsx @@ -363,6 +363,7 @@ function SidebarContent({ pathname.startsWith(item.href + "/")); return ( + {/* + Loading guard: InlineLoader is meant for card/section-level loading + (py-4 + message text) and doesn't fit a small inline pill here, so + we use the same underlying the project already + ships, just placed inside the existing badge shell. + */}
- {user?.role ?? "—"} + {loading ? ( + + ) : ( + user?.role ?? "—" + )}
+ + ); + } + + // ── Render ──────────────────────────────────────────────────────────────── + return ( + <> + + + {/* Edit modal */} + {editOpen && ( + setEditOpen(false)} + onSubmit={handleUpdateSubmit} + /> + )} + + {/* Delete confirmation */} + { if (!deleting) setDeleteOpen(false); }} + onConfirm={handleDeleteConfirm} + title="حذف العميل" + description={`هل أنت متأكد من حذف ${address.label}؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء.`} + /> + +
+ + {/* ── Page header ── */} +
+ {/* Back link */} + + +

+ تفاصيل العنوان +

+ +
+
+

+ {address.label} +

+ {client && ( +

+ {client.name} +

+ )} +
+ + {/* Action buttons */} +
+ + + +
+
+
+ + {/* ── Address details component ── */} + + +
+ + ); +} \ No newline at end of file diff --git a/app/dashboard/clients/[clientId]/addresses/page.tsx b/app/dashboard/clients/[clientId]/addresses/page.tsx index 2111610..4021d1e 100644 --- a/app/dashboard/clients/[clientId]/addresses/page.tsx +++ b/app/dashboard/clients/[clientId]/addresses/page.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; -import { Alert, Toast, ArchiveButton } from "@/src/Components/UI"; +import { Alert, ConfirmDialog, Toast, ArchiveButton } from "@/src/Components/UI"; import { useClientAddresses } from "@/src/hooks/useClientAddresses"; import { clientService } from "@/src/services/client.service"; @@ -12,7 +12,7 @@ import { getStoredToken } from "@/src/lib/auth"; import type { Client, ClientFormData } from "@/src/types/client"; import type { ClientAddress } from "@/src/types/client_adresses"; -import { AddressFormModal, DeleteConfirmModal, ClientFormModal } from "@/src/Components/Client"; +import { AddressFormModal, ClientFormModal } from "@/src/Components/Client"; import { ArchivedClientsAddressesModal } from "@/src/Components/Client_Adress/archive/ArchivedClientsAddressesModal"; import type { CreateAddressFormValues, @@ -443,23 +443,14 @@ export default function ClientAddressesPage() { )} {/* Delete confirmation */} - {deleteTarget && ( - { if (!deleting) setDeleteTarget(null); }} - onConfirm={handleDeleteConfirm} - /> - )} + { if (!deleting) setDeleteTarget(null); }} + onConfirm={handleDeleteConfirm} + title="حذف العميل" + description={`هل أنت متأكد من حذف ${deleteTarget?.label ?? ""}؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء.`} + /> {/* Client edit modal */} {editingClient && client && ( diff --git a/app/dashboard/clients/page.tsx b/app/dashboard/clients/page.tsx index dd5ebf5..ed1ab26 100644 --- a/app/dashboard/clients/page.tsx +++ b/app/dashboard/clients/page.tsx @@ -6,14 +6,12 @@ import { useRouter } from "next/navigation"; // ── UI components ────────────────────────────────────────────────────────── // NOTE: Toast is now imported from the canonical UI barrel, NOT from Client/Toast. -import { Alert, Toast, ArchiveButton } from "@/src/Components/UI"; +import { Alert, Toast, ArchiveButton, ConfirmDialog } from "@/src/Components/UI"; // ── Client-specific components ───────────────────────────────────────────── import { useClients } from "@/src/hooks/useClients"; import type { Client, ClientFormData } from "@/src/types/client"; -import { ClientFormModal } from "@/src/Components/Client/Clientformmodal"; -import { ClientTable } from "@/src/Components/Client/Clienttable"; -import { DeleteConfirmModal } from "@/src/Components/Client/Deleteconfirmmodal"; +import { ClientFormModal , ClientTable } from "@/src/Components/Client"; import { ArchivedClientsModal } from "@/src/Components/Client/archive/ArchivedClientsModal"; @@ -82,14 +80,14 @@ export default function ClientsPage() { )} {/* Delete confirmation dialog */} - {deleteTarget && ( - { if (!deleting) setDeleteTarget(null); }} - onConfirm={handleDeleteConfirm} - /> - )} + { if (!deleting) setDeleteTarget(null); }} + onConfirm={handleDeleteConfirm} + title="حذف العميل" + description={`هل أنت متأكد من حذف ${deleteTarget?.name ?? ""}؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء.`} + /> {/* Archive browser modal */} {archiveOpen && ( @@ -258,4 +256,4 @@ export default function ClientsPage() { setArchiveOpen(true)} label="أرشيف العملاء" /> ); -} +} \ No newline at end of file diff --git a/app/dashboard/drivers/[driverId]/page.tsx b/app/dashboard/drivers/[driverId]/page.tsx index 49da6e0..7484f7a 100644 --- a/app/dashboard/drivers/[driverId]/page.tsx +++ b/app/dashboard/drivers/[driverId]/page.tsx @@ -2,18 +2,14 @@ import { useCallback, useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; -import { Spinner } from "@/src/Components/UI"; -import { DriverFormModal } from "@/src/Components/Driver/DriverFormModal"; -import { DriverDeleteModal } from "@/src/Components/Driver/DriverDeleteModal"; +import { ConfirmDialog, Spinner } from "@/src/Components/UI"; +import { DriverFormModal , PhotoCard } from "@/src/Components/Driver"; import { DriverReportPanel } from "@/src/Components/Driver_Report/driverReport"; import type { Driver, CreateDriverPayload, UpdateDriverPayload } from "@/src/types/driver"; import { DRIVER_STATUS_MAP, DRIVER_CARD_TYPE_MAP, NATIONAL_ID_TYPE_MAP } from "@/src/types/driver"; -import { PhotoCard } from "@/src/Components/Driver/DriverPhotos"; import { driverService } from "@/src/services"; import { getStoredToken } from "@/src/lib/auth"; -// ── Helpers ─────────────────────────────────────────────────────────────────── - function fmtDate(iso?: string | null): string { if (!iso) return "—"; return new Date(iso).toLocaleDateString("ar-SA", { @@ -26,8 +22,6 @@ function isExpiringSoon(iso?: string | null): boolean { return (new Date(iso).getTime() - Date.now()) <= 90 * 86_400_000; } -// ── Sub-components ──────────────────────────────────────────────────────────── - function SectionCard({ title, children }: { title: string; children: React.ReactNode }) { return (
(null); const [avatarError, setAvatarError] = useState(false); - // Reset avatar error when photo changes after an update useEffect(() => { queueMicrotask(() => setAvatarError(false)); }, [driver?.photoUrl]); - // Toast shown after edit/delete actions on this page const [toast, setToast] = useState<{ type: "success" | "error"; message: string } | null>(null); const [editOpen, setEditOpen] = useState(false); @@ -102,15 +90,14 @@ export default function DriverDetailPage() { setTimeout(() => setToast(null), 4000); }, []); - // ── Load driver ─────────────────────────────────────────────────────────── const loadDriver = useCallback(async () => { if (!driverId) return; setLoading(true); setError(null); try { const token = getStoredToken(); - const res = await driverService.getById(driverId, token); - setDriver((res as unknown as { data: Driver }).data); + const data = await driverService.getById(driverId, token); + setDriver(data); } catch (err: unknown) { setError(err instanceof Error ? err.message : "تعذّر تحميل بيانات السائق."); } finally { @@ -120,7 +107,6 @@ export default function DriverDetailPage() { useEffect(() => { queueMicrotask(loadDriver); }, [loadDriver]); - // ── Edit submit ─────────────────────────────────────────────────────────── const handleEditSubmit = useCallback( async (payload: CreateDriverPayload | UpdateDriverPayload): Promise => { if (!driver) return false; @@ -132,13 +118,11 @@ export default function DriverDetailPage() { const hasFiles = typedPayload.photo || typedPayload.nationalPhoto || typedPayload.driverCardPhoto; if (hasFiles) { - // Use multipart — JSON.stringify converts File to {} which fails validation await driverService.updateWithImages(driver.id, typedPayload, token); } else { await driverService.update(driver.id, typedPayload, token); } - // Re-fetch to reflect updated status, name, photos, etc. await loadDriver(); showToast("success", "تم تحديث بيانات السائق بنجاح."); return true; @@ -151,7 +135,6 @@ export default function DriverDetailPage() { [driver, loadDriver, showToast], ); - // ── Delete confirm ──────────────────────────────────────────────────────── const handleConfirmDelete = useCallback(async () => { if (!driver) return; setDeleting(true); @@ -168,7 +151,6 @@ export default function DriverDetailPage() { const statusConfig = driver ? DRIVER_STATUS_MAP[driver.status] : null; - // ── Render: Loading ─────────────────────────────────────────────────────── if (loading) { return (
@@ -178,7 +160,6 @@ export default function DriverDetailPage() { ); } - // ── Render: Error ───────────────────────────────────────────────────────── if (error || !driver) { return (
@@ -195,12 +176,10 @@ export default function DriverDetailPage() { ); } - // ── Render: Driver detail ───────────────────────────────────────────────── return ( <>
- {/* ── Toast notification ── */} {toast && (
)} - {/* ── Page header ── */}
- {/* ── Content grid ── */}
@@ -380,14 +357,14 @@ export default function DriverDetailPage() { /> )} - {deleteOpen && ( - setDeleteOpen(false)} - onConfirm={handleConfirmDelete} - /> - )} + setDeleteOpen(false)} + onConfirm={handleConfirmDelete} + title="حذف السائق" + description={`هل أنت متأكد من حذف ${driver.name} (${driver.phone})؟ لا يمكن التراجع عن هذا الإجراء.`} + /> ); } \ No newline at end of file diff --git a/app/dashboard/drivers/page.tsx b/app/dashboard/drivers/page.tsx index 58ed8ff..d42c735 100644 --- a/app/dashboard/drivers/page.tsx +++ b/app/dashboard/drivers/page.tsx @@ -1,13 +1,11 @@ "use client"; import { useState, useCallback } from "react"; -import { Alert, Spinner, ArchiveButton } from "@/src/Components/UI"; -import { DriverFormModal } from "@/src/Components/Driver/DriverFormModal"; -import { DriverDeleteModal } from "@/src/Components/Driver/DriverDeleteModal"; +import { Alert, Spinner, ArchiveButton, ConfirmDialog } from "@/src/Components/UI"; import { ArchivedDrivers } from "@/src/Components/Driver/archive/ArchivedDrivers"; import { useDrivers } from "@/src/hooks/useDriver"; import { CreateDriverPayload, Driver, DRIVER_STATUS_MAP, UpdateDriverPayload } from "@/src/types/driver"; -import { DriverDetailPanel } from "@/src/Components/Driver/DriverDetailPanel"; +import { DriverDetailPanel, DriverFormModal , } from "@/src/Components/Driver"; // ── Helpers ─────────────────────────────────────────────────────────────────── @@ -413,14 +411,14 @@ export default function DriversPage() { )} {/* ── Delete modal ── */} - {deleteTarget && ( - setDeleteTarget(null)} - onConfirm={handleConfirmDelete} - /> - )} + setDeleteTarget(null)} + onConfirm={handleConfirmDelete} + title="حذف السائق" + description={`هل أنت متأكد من حذف ${deleteTarget?.name ?? ""} (${deleteTarget?.phone ?? ""})؟ لا يمكن التراجع عن هذا الإجراء.`} + /> {/* ── Archive browser modal ── */} {archiveOpen && ( diff --git a/app/dashboard/orders/page.tsx b/app/dashboard/orders/page.tsx index 50792a3..962f3fa 100644 --- a/app/dashboard/orders/page.tsx +++ b/app/dashboard/orders/page.tsx @@ -3,13 +3,12 @@ import { useState, useCallback } from "react"; -import { Alert, Spinner, Toast, ArchiveButton } from "@/src/Components/UI"; -import { OrderFormModal } from "@/src/Components/Order/OrderFormModal"; -import { OrderDeleteModal } from "@/src/Components/Order/OrderDeleteModal"; -import { OrderDetailPanel, ORDER_STATUS_MAP } from "@/src/Components/Order/OrderDetailBanel"; +import { Alert, Spinner, Toast, ArchiveButton, ConfirmDialog } from "@/src/Components/UI"; +import { ORDER_STATUS_MAP } from "@/src/Components/Order/OrderDetailBanel"; import { ArchivedOrdersModal } from "@/src/Components/Order/archive/ArchivedOrdersModal"; import { useOrders } from "@/src/hooks/useOrder"; import type { CreateOrderPayload, Order, UpdateOrderPayload } from "@/src/types/order"; +import { OrderFormModal , OrderDetailPanel} from "@/src/Components/Order"; // ── Helpers — copied verbatim from app/dashboard/drivers/page.tsx ─────── @@ -441,14 +440,14 @@ export default function OrderComponent() { )} {/* ── Delete modal ── */} - {deleteTarget && ( - setDeleteTarget(null)} - onConfirm={handleConfirmDelete} - /> - )} + setDeleteTarget(null)} + onConfirm={handleConfirmDelete} + title="حذف الطلب" + description={`هل أنت متأكد من حذف الطلب ${deleteTarget?.shipmentNumber ?? ""} الخاص بـ ${deleteTarget?.recipientName ?? ""}؟ لا يمكن التراجع عن هذا الإجراء.`} + /> {/* ── Archive browser modal ── */} {archiveOpen && ( diff --git a/app/dashboard/roles/page.tsx b/app/dashboard/roles/page.tsx index 7b84745..04adb45 100644 --- a/app/dashboard/roles/page.tsx +++ b/app/dashboard/roles/page.tsx @@ -1,12 +1,9 @@ "use client"; import { useState } from "react"; -import { Alert, ArchiveButton } from "@/src/Components/UI"; +import { Alert, ArchiveButton, ConfirmDialog,Toast } from "@/src/Components/UI"; import { RoleTable } from "@/src/Components/role/RoleTable"; -import { RoleFormModal } from "@/src/Components/role/RoleFormModal"; -import { RoleDetailModal } from "@/src/Components/role/RoleDetailModal"; -import { DeleteRoleModal } from "@/src/Components/role/DeleteRoleModal"; -import { Toast } from "@/src/Components/UI"; +import { RoleFormModal,RoleDetailModal } from "@/src/Components/role"; import { useRoles } from "@/src/hooks/useRole"; import { Role, RoleFormData } from "@/src/types/role"; import { ArchivedRolesModal } from "@/src/Components/role/archive/ArchivedRolesModal"; @@ -77,14 +74,14 @@ export default function RolesPage() { )} {/* Delete confirmation */} - {deleteTarget && ( - { if (!deleting) setDeleteTarget(null); }} - onConfirm={handleDeleteConfirm} - /> - )} + { if (!deleting) setDeleteTarget(null); }} + onConfirm={handleDeleteConfirm} + title="حذف الدور" + description={`هل أنت متأكد من حذف دور ${deleteTarget?.name ?? ""}؟ لا يمكن التراجع عن هذا الإجراء.`} + /> {/* Archive browser modal */} {archiveOpen && ( diff --git a/app/dashboard/trips/[tripId]/page.tsx b/app/dashboard/trips/[tripId]/page.tsx index 944feab..0ea45ac 100644 --- a/app/dashboard/trips/[tripId]/page.tsx +++ b/app/dashboard/trips/[tripId]/page.tsx @@ -2,9 +2,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { useParams, useRouter } from "next/navigation"; -import { Spinner } from "@/src/Components/UI"; +import { ConfirmDialog, Spinner } from "@/src/Components/UI"; import { TripFormModal } from "@/src/Components/Trip/Tripformmodal"; -import { TripDeleteModal } from "@/src/Components/Trip/Tripdeletemodal"; import { TripReportPanel } from "@/src/Components/Trip_Report/Tripreportpanel"; import { tripService } from "@/src/services/trip.service"; import { getStoredToken } from "@/src/lib/auth"; @@ -206,44 +205,42 @@ export default function TripDetailPage() { // ── Load trip ───────────────────────────────────────────────────────────── const loadTrip = useCallback(async () => { - if (!tripId) return; - setLoading(true); - setError(null); - try { - const token = getStoredToken(); - const res = await tripService.getById(tripId, token); - setTrip((res as unknown as { data: Trip }).data); - } catch (err: unknown) { - setError(err instanceof Error ? err.message : "تعذّر تحميل بيانات الرحلة."); - } finally { - setLoading(false); - } - }, [tripId]); - + if (!tripId) return; + setLoading(true); + setError(null); + try { + const token = getStoredToken(); + const data = await tripService.getById(tripId, token); + setTrip(data); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : "تعذّر تحميل بيانات الرحلة."); + } finally { + setLoading(false); + } +}, [tripId]); useEffect(() => { queueMicrotask(loadTrip); }, [loadTrip]); // ── Edit submit ─────────────────────────────────────────────────────────── const handleEditSubmit = useCallback( - async ( - payload: CreateTripPayload | UpdateTripPayload, - ): Promise => { - if (!trip) return false; - try { - const token = getStoredToken(); - const res = await tripService.update(trip.id, payload as UpdateTripPayload, token); - const updated = (res as unknown as { data: Trip }).data; - setTrip(updated); - notify({ type: "success", message: "تم تحديث بيانات الرحلة بنجاح." }); - return true; - } catch (err) { - notify({ type: "error", message: extractApiMessage(err, "تعذّر تحديث الرحلة.") }); - return false; - } - }, - [trip, notify], - ); + async ( + payload: CreateTripPayload | UpdateTripPayload, + ): Promise => { + if (!trip) return false; + try { + const token = getStoredToken(); + const updated = await tripService.update(trip.id, payload as UpdateTripPayload, token); + setTrip(updated); + notify({ type: "success", message: "تم تحديث بيانات الرحلة بنجاح." }); + return true; + } catch (err) { + notify({ type: "error", message: extractApiMessage(err, "تعذّر تحديث الرحلة.") }); + return false; + } + }, + [trip, notify], +); // ── Delete confirm ──────────────────────────────────────────────────────── const handleConfirmDelete = useCallback(async () => { @@ -558,14 +555,14 @@ export default function TripDetailPage() { )} {/* ── Delete modal ── */} - {deleteOpen && ( - setDeleteOpen(false)} - onConfirm={handleConfirmDelete} - /> - )} + setDeleteOpen(false)} + onConfirm={handleConfirmDelete} + title="حذف الرحلة" + description={`هل أنت متأكد من حذف رحلة ${trip.title} (${trip.tripNumber})؟ لا يمكن التراجع عن هذا الإجراء.`} + /> ); } \ No newline at end of file diff --git a/app/dashboard/trips/archived/[tripId]/page.tsx b/app/dashboard/trips/archived/[tripId]/page.tsx index 3ffbe63..5837b83 100644 --- a/app/dashboard/trips/archived/[tripId]/page.tsx +++ b/app/dashboard/trips/archived/[tripId]/page.tsx @@ -31,10 +31,8 @@ export default function ArchivedTripDetailPage() { setError(null); try { const token = getStoredToken(); - // Archived trips must go through the archived endpoint — - // the normal /trip/:id endpoint won't return soft-deleted trips. - const res = await tripService.getArchivedById(tripId, token); - if (!cancelled) setTrip((res as unknown as { data: Trip }).data); + const data = await tripService.getArchivedById(tripId, token); + if (!cancelled) setTrip(data); } catch { if (!cancelled) setError("لم يتم العثور على هذه الرحلة في الأرشيف."); } finally { @@ -54,7 +52,6 @@ export default function ArchivedTripDetailPage() { ); } - // Edge case: invalid/missing/unreachable trip ID if (error || !trip) { return ( setDeleteTarget(null)} - onConfirm={handleConfirmDelete} - /> - )} + setDeleteTarget(null)} + onConfirm={handleConfirmDelete} + title="حذف الرحلة" + description={`هل أنت متأكد من حذف رحلة ${deleteTarget?.title ?? ""} (${deleteTarget?.tripNumber ?? ""})؟ لا يمكن التراجع عن هذا الإجراء.`} + /> {/* ── Archive browser modal ── */} {archiveOpen && ( @@ -768,4 +766,4 @@ export default function TripsPage() {
); -} +} \ No newline at end of file diff --git a/app/dashboard/users/page.tsx b/app/dashboard/users/page.tsx index 1482c4e..8af9c8a 100644 --- a/app/dashboard/users/page.tsx +++ b/app/dashboard/users/page.tsx @@ -1,11 +1,8 @@ "use client"; import { useState } from "react"; -import { Alert, Toast, ArchiveButton } from "@/src/Components/UI"; -import { UserTable } from "@/src/Components/User/UserTable"; -import { UserFormModal } from "@/src/Components/User/UserFormModal"; -import { UserDetailModal } from "@/src/Components/User/UserDetailModal"; -import { DeleteConfirmModal } from "@/src/Components/User/DeleteConfirmModal"; +import { Alert, Toast, ArchiveButton, ConfirmDialog } from "@/src/Components/UI"; +import { UserFormModal,UserDetailModal,UserTable } from "@/src/Components/User"; import { useUsers } from "@/src/hooks/useUser"; import type { User, UserFormData } from "@/src/types/user"; import { ArchivedUsersModal } from "@/src/Components/User/archive/Archivedusersmodal"; @@ -76,16 +73,14 @@ export default function UsersPage() { )} {/* Delete confirmation dialog */} - {deleteTarget && ( - { - if (!deleting) setDeleteTarget(null); - }} - onConfirm={handleDeleteConfirm} - /> - )} + { if (!deleting) setDeleteTarget(null); }} + onConfirm={handleDeleteConfirm} + title="حذف المستخدم" + description={`هل أنت متأكد من حذف ${deleteTarget?.name ?? ""}؟ لا يمكن التراجع عن هذا الإجراء.`} + /> {/* Archive browser modal */} {archiveOpen && ( diff --git a/app/layout.tsx b/app/layout.tsx index c5b1f31..da6e053 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -18,7 +18,7 @@ export default function RootLayout({ return ( // ⚠️ اتصلح: الـ Navbar والـ footer كانوا برة / وده HTML غير صالح - + {/* الناف بار بيظهر بس لو مفيش يوزر مسجل دخول */} {children} diff --git a/src/Components/Branch/BranchDetailModal.tsx b/src/Components/Branch/BranchDetailModal.tsx index bf81ddd..b7fd5c3 100644 --- a/src/Components/Branch/BranchDetailModal.tsx +++ b/src/Components/Branch/BranchDetailModal.tsx @@ -78,20 +78,20 @@ export function BranchDetailModal({ branchId, onClose }: BranchDetailModalProps) // fetch branch details on mount useEffect(() => { - let cancelled = false; - (async () => { - try { - const token = getStoredToken(); - const res = await branchService.getById(branchId, token); - if (!cancelled) setBranch(res.data); - } catch { - if (!cancelled) setError("تعذّر تحميل بيانات الفرع. يرجى المحاولة لاحقاً."); - } finally { - if (!cancelled) setLoading(false); - } - })(); - return () => { cancelled = true; }; - }, [branchId]); + let cancelled = false; + (async () => { + try { + const token = getStoredToken(); + const data = await branchService.getById(branchId, token); + if (!cancelled) setBranch(data); + } catch { + if (!cancelled) setError("تعذّر تحميل بيانات الفرع. يرجى المحاولة لاحقاً."); + } finally { + if (!cancelled) setLoading(false); + } + })(); + return () => { cancelled = true; }; +}, [branchId]); // ── helpers ─────────────────────────────────────────────────────────────── const fmt = (iso?: string | null) => diff --git a/src/Components/Branch/DeleteConfirmModal.tsx b/src/Components/Branch/DeleteConfirmModal.tsx deleted file mode 100644 index 22dc4f9..0000000 --- a/src/Components/Branch/DeleteConfirmModal.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { Spinner } from "../UI"; -import type { Branch } from "@/src/types/branch"; - -interface DeleteConfirmModalProps { - branch: Branch; - deleting: boolean; - onCancel: () => void; - onConfirm: () => void; -} - -export function DeleteConfirmModal({ branch, deleting, onCancel, onConfirm }: DeleteConfirmModalProps) { - useEffect(() => { - const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onCancel(); }; - window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); - }, [onCancel]); - - return ( -
{ if (e.target === e.currentTarget) onCancel(); }} - style={{ position: "fixed", inset: 0, zIndex: 60, background: "rgba(15,23,42,0.55)", backdropFilter: "blur(4px)", display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem" }} - > -
e.stopPropagation()} - style={{ width: "100%", maxWidth: 400, background: "var(--color-surface)", borderRadius: "var(--radius-xl)", border: "1px solid #FECACA", boxShadow: "0 20px 48px rgba(0,0,0,.18)", padding: "2rem", display: "flex", flexDirection: "column", gap: "1rem", textAlign: "center" }} - > - {/* icon delete */} -
- - - - -
-
-

حذف الفرع

-

- هل أنت متأكد من حذف فرع {branch.name}؟ لا يمكن التراجع عن هذا الإجراء. -

-
-
- - -
-
-
- ); -} \ No newline at end of file diff --git a/src/Components/Car_Maintanance/CarMaintananceDeleteModal.tsx b/src/Components/Car_Maintanance/CarMaintananceDeleteModal.tsx deleted file mode 100644 index 856150a..0000000 --- a/src/Components/Car_Maintanance/CarMaintananceDeleteModal.tsx +++ /dev/null @@ -1,91 +0,0 @@ - "use client"; - -import { useEffect } from "react"; -import { Spinner } from "../UI"; -import { fmtCost } from "@/src/types/carMaintanance"; -import type { CarMaintenance } from "@/src/types/carMaintanance"; - -interface CarMaintenanceDeleteModalProps { - record: CarMaintenance; - deleting: boolean; - onCancel: () => void; - onConfirm: () => void; -} - -export function CarMaintenanceDeleteModal({ - record, - deleting, - onCancel, - onConfirm, -}: CarMaintenanceDeleteModalProps) { - useEffect(() => { - const h = (e: KeyboardEvent) => { if (e.key === "Escape") onCancel(); }; - window.addEventListener("keydown", h); - return () => window.removeEventListener("keydown", h); - }, [onCancel]); - - return ( -
{ if (e.target === e.currentTarget) onCancel(); }} - style={{ - position: "fixed", inset: 0, zIndex: 60, - background: "rgba(15,23,42,0.55)", backdropFilter: "blur(4px)", - display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem", - }} - > -
e.stopPropagation()} - style={{ - width: "100%", maxWidth: 420, - background: "var(--color-surface)", - borderRadius: "var(--radius-xl)", - border: "1px solid #FECACA", - boxShadow: "0 20px 48px rgba(0,0,0,.18)", - padding: "2rem", - display: "flex", flexDirection: "column", gap: "1rem", - textAlign: "center", - }} - > - {/* Icon */} -
- - - - - - -
- -
-

- حذف سجل الصيانة -

-

- هل أنت متأكد من حذف سجل{" "} - - {record.reason} - - {" "}( - - {fmtCost(record.cost)} - - )؟ لا يمكن التراجع عن هذا الإجراء. -

-
- -
- - -
-
-
- ); -} \ No newline at end of file diff --git a/src/Components/Client/Deleteconfirmmodal.tsx b/src/Components/Client/Deleteconfirmmodal.tsx deleted file mode 100644 index 20e805d..0000000 --- a/src/Components/Client/Deleteconfirmmodal.tsx +++ /dev/null @@ -1,171 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { Spinner } from "../UI"; -import type { Client } from "@/src/types/client"; - -interface DeleteConfirmModalProps { - client: Client; - deleting: boolean; - onCancel: () => void; - onConfirm: () => void; -} - -export function DeleteConfirmModal({ - client, - deleting, - onCancel, - onConfirm, -}: DeleteConfirmModalProps) { - // Close on Escape — identical to User/DeleteConfirmModal - useEffect(() => { - const handler = (e: KeyboardEvent) => { - if (e.key === "Escape") onCancel(); - }; - window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); - }, [onCancel]); - - return ( -
{ - if (e.target === e.currentTarget) onCancel(); - }} - style={{ - position: "fixed", - inset: 0, - zIndex: 60, - background: "rgba(15,23,42,0.55)", - backdropFilter: "blur(4px)", - display: "flex", - alignItems: "center", - justifyContent: "center", - padding: "1rem", - }} - > -
e.stopPropagation()} - style={{ - width: "100%", - maxWidth: 400, - background: "var(--color-surface)", - borderRadius: "var(--radius-xl)", - border: "1px solid #FECACA", - boxShadow: "0 20px 48px rgba(0,0,0,.18)", - padding: "2rem", - display: "flex", - flexDirection: "column", - gap: "1rem", - textAlign: "center", - }} - > - {/* أيقونة الحذف */} -
- - - - - - -
- -
-

- حذف العميل -

-

- هل أنت متأكد من حذف{" "} - - {client.name} - - ؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء. -

-
- -
- - -
-
-
- ); -} \ No newline at end of file diff --git a/src/Components/Driver/DriverDeleteModal.tsx b/src/Components/Driver/DriverDeleteModal.tsx deleted file mode 100644 index 98b7d3c..0000000 --- a/src/Components/Driver/DriverDeleteModal.tsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client"; - - -import { useEffect } from "react"; -import { Spinner } from "../UI"; -import type { Driver } from "@/src/types/driver"; - -interface DriverDeleteModalProps { - driver: Driver; - deleting: boolean; - onCancel: () => void; - onConfirm: () => void; -} - -export function DriverDeleteModal({ driver, deleting, onCancel, onConfirm }: DriverDeleteModalProps) { - useEffect(() => { - const h = (e: KeyboardEvent) => { if (e.key === "Escape") onCancel(); }; - window.addEventListener("keydown", h); - return () => window.removeEventListener("keydown", h); - }, [onCancel]); - - return ( -
{ if (e.target === e.currentTarget) onCancel(); }} - style={{ - position: "fixed", inset: 0, zIndex: 60, - background: "rgba(15,23,42,0.55)", backdropFilter: "blur(4px)", - display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem", - }} - > -
e.stopPropagation()} - style={{ - width: "100%", maxWidth: 420, - background: "var(--color-surface)", - borderRadius: "var(--radius-xl)", - border: "1px solid #FECACA", - boxShadow: "0 20px 48px rgba(0,0,0,.18)", - padding: "2rem", - display: "flex", flexDirection: "column", gap: "1rem", - textAlign: "center", - }} - > - {/* Icon */} -
- - - - - - -
- -
-

- حذف السائق -

-

- هل أنت متأكد من حذف{" "} - {driver.name} - {" "}( - - {driver.phone} - - )؟ لا يمكن التراجع عن هذا الإجراء. -

-
- -
- - -
-
-
- ); -} \ No newline at end of file diff --git a/src/Components/Driver/DriverDetailPanel.tsx b/src/Components/Driver/DriverDetailPanel.tsx index 35d986a..a6df45f 100644 --- a/src/Components/Driver/DriverDetailPanel.tsx +++ b/src/Components/Driver/DriverDetailPanel.tsx @@ -12,8 +12,6 @@ import { NATIONAL_ID_TYPE_MAP, } from "@/src/types/driver"; -// ── Helpers ─────────────────────────────────────────────────────────────────── - function fmtDate(iso?: string | null): string { if (!iso) return "—"; return new Date(iso).toLocaleDateString("ar-SA", { @@ -28,8 +26,6 @@ function isExpiringSoon(iso?: string | null): boolean { return new Date(iso).getTime() - Date.now() <= 90 * 86_400_000; } -// ── Sub-components ──────────────────────────────────────────────────────────── - function DetailRow({ label, value, @@ -89,10 +85,6 @@ function SectionHeading({ title }: { title: string }) { ); } -// ── PhotoCard ───────────────────────────────────────────────────────────────── -// key={url} on the forces a full remount whenever the URL changes, -// which clears any stale onError state from a previously failed load. - function PhotoCard({ url, label }: { url?: string | null; label: string }) { const [imgError, setImgError] = useState(false); @@ -161,8 +153,6 @@ function PhotoCard({ url, label }: { url?: string | null; label: string }) { ); } -// ── Props ───────────────────────────────────────────────────────────────────── - interface DriverDetailPanelProps { driverId: string; onClose: () => void; @@ -170,8 +160,6 @@ interface DriverDetailPanelProps { onDelete: (driver: Driver) => void; } -// ── Component ───────────────────────────────────────────────────────────────── - export function DriverDetailPanel({ driverId, onClose, @@ -187,8 +175,8 @@ export function DriverDetailPanel({ setError(null); try { const token = getStoredToken(); - const res = await driverService.getById(driverId, token); - setDriver((res as unknown as { data: Driver }).data); + const data = await driverService.getById(driverId, token); + setDriver(data); } catch (err: unknown) { setError(err instanceof Error ? err.message : "تعذّر تحميل بيانات السائق."); } finally { @@ -208,7 +196,6 @@ export function DriverDetailPanel({ return ( <> - {/* Backdrop */}
- {/* Slide-in panel */}