fix: resolve permission fallback bug, remove debug code, and cleanup duplication
Logic Errors ------------ - Sidebar: fix permissions fallback so a user with no permissions gets zero access instead of falling back to the full nav list (user?.permissions ?? [] instead of ?? navSections.flatMap(...)) - carMaintanance.service / UseCarsMaintanance: add pagination to the maintenance records fetch instead of loading the entire list at once - api.ts: redact sensitive fields (password, token) before logging the request body in console.debug, to avoid leaking credentials/PII Code Flow Problems ------------------- - OrderFormModal: remove leftover console.log/debug statements and the redundant onClick handler on the submit button - auth: remove the unused src/service/auth.service.ts (axios) and keep only src/services/auth.service.ts (fetch); fix useAuth.ts import path from
This commit is contained in:
@@ -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 (
|
||||
<div style={{
|
||||
@@ -73,10 +67,6 @@ function DetailRow({ label, value, mono = false, warn = false }: {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── Page ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
export default function DriverDetailPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
@@ -86,11 +76,9 @@ export default function DriverDetailPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(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<boolean> => {
|
||||
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 (
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 12, padding: "6rem 0", color: "var(--color-text-muted)" }}>
|
||||
@@ -178,7 +160,6 @@ export default function DriverDetailPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Render: Error ─────────────────────────────────────────────────────────
|
||||
if (error || !driver) {
|
||||
return (
|
||||
<div style={{ maxWidth: 480, margin: "4rem auto", borderRadius: "var(--radius-xl)", border: "1px solid #FECACA", background: "#FEF2F2", padding: "1.5rem", textAlign: "center" }}>
|
||||
@@ -195,12 +176,10 @@ export default function DriverDetailPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Render: Driver detail ─────────────────────────────────────────────────
|
||||
return (
|
||||
<>
|
||||
<section style={{ display: "flex", flexDirection: "column", gap: "1.5rem" }}>
|
||||
|
||||
{/* ── Toast notification ── */}
|
||||
{toast && (
|
||||
<div style={{
|
||||
borderRadius: "var(--radius-lg)",
|
||||
@@ -215,7 +194,6 @@ export default function DriverDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Page header ── */}
|
||||
<header style={{
|
||||
borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border)",
|
||||
background: "var(--color-surface)", padding: "1.5rem 2rem", boxShadow: "var(--shadow-card)",
|
||||
@@ -294,7 +272,6 @@ export default function DriverDetailPage() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* ── Content grid ── */}
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1.5rem" }}>
|
||||
<SectionCard title="البيانات الشخصية">
|
||||
<DetailRow label="الاسم الكامل" value={driver.name} />
|
||||
@@ -380,14 +357,14 @@ export default function DriverDetailPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{deleteOpen && (
|
||||
<DriverDeleteModal
|
||||
driver={driver}
|
||||
deleting={deleting}
|
||||
onCancel={() => setDeleteOpen(false)}
|
||||
onConfirm={handleConfirmDelete}
|
||||
/>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
open={deleteOpen}
|
||||
loading={deleting}
|
||||
onCancel={() => setDeleteOpen(false)}
|
||||
onConfirm={handleConfirmDelete}
|
||||
title="حذف السائق"
|
||||
description={`هل أنت متأكد من حذف ${driver.name} (${driver.phone})؟ لا يمكن التراجع عن هذا الإجراء.`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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 && (
|
||||
<DriverDeleteModal
|
||||
driver={deleteTarget}
|
||||
deleting={deleting}
|
||||
onCancel={() => setDeleteTarget(null)}
|
||||
onConfirm={handleConfirmDelete}
|
||||
/>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
open={!!deleteTarget}
|
||||
loading={deleting}
|
||||
onCancel={() => setDeleteTarget(null)}
|
||||
onConfirm={handleConfirmDelete}
|
||||
title="حذف السائق"
|
||||
description={`هل أنت متأكد من حذف ${deleteTarget?.name ?? ""} (${deleteTarget?.phone ?? ""})؟ لا يمكن التراجع عن هذا الإجراء.`}
|
||||
/>
|
||||
|
||||
{/* ── Archive browser modal ── */}
|
||||
{archiveOpen && (
|
||||
|
||||
Reference in New Issue
Block a user