From fa49ba88173b1d896f0ceda71dbb2a4bedeb37ba Mon Sep 17 00:00:00 2001
From: m7amedez511
Date: Wed, 22 Jul 2026 16:30:20 +0300
Subject: [PATCH] The client files, client address files, and order files have
been modified to implement all CRUD operations and endpoints related to the
archive.
---
.../addresses/[addressesId]/page.tsx | 375 ++++--------------
.../clients/[clientId]/addresses/page.tsx | 281 +++++++++----
app/dashboard/clients/page.tsx | 2 +-
app/dashboard/orders/page.tsx | 241 +----------
src/Components/Client/Toast.tsx | 5 -
.../archive/ArchivedClientDetailModal.tsx | 2 +-
src/Components/Client/index.ts | 1 -
...erDetailBanel.tsx => OrderDetailModel.tsx} | 0
src/Components/Order/OrderFormModal.tsx | 19 +-
src/Components/Order/OrderTable.tsx | 248 ++++++++++++
.../archive/ArchivedOrderDetailModal.tsx | 3 +-
.../Order/archive/ArchivedOrderTable.tsx | 2 +-
src/Components/Order/index.ts | 3 +-
src/validations/order.validation.ts | 47 ++-
14 files changed, 601 insertions(+), 628 deletions(-)
delete mode 100644 src/Components/Client/Toast.tsx
rename src/Components/Order/{OrderDetailBanel.tsx => OrderDetailModel.tsx} (100%)
create mode 100644 src/Components/Order/OrderTable.tsx
diff --git a/app/dashboard/clients/[clientId]/addresses/[addressesId]/page.tsx b/app/dashboard/clients/[clientId]/addresses/[addressesId]/page.tsx
index 5291e4c..39a3e02 100644
--- a/app/dashboard/clients/[clientId]/addresses/[addressesId]/page.tsx
+++ b/app/dashboard/clients/[clientId]/addresses/[addressesId]/page.tsx
@@ -1,339 +1,100 @@
+// app/dashboard/[clientId]/addresses/[addressesId]/page.tsx
"use client";
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
-import { Alert, ConfirmDialog, Toast } from "@/src/Components/UI";
-import { AddressFormModal } from "@/src/Components/Client";
-import { AddressDetails } from "@/src/Components/Client_Adress/AddressDetails";
-import { clientAddressService } from "@/src/services/clientAddress.service";
-import { clientService } from "@/src/services/client.service";
-import { getStoredToken } from "@/src/lib/auth";
+import { Alert, Spinner, Button } from "@/src/Components/UI";
+import { AddressDetails } from "@/src/Components/Client_Adress/AddressDetails";
+import { clientAddressService } from "@/src/services/clientAddress.service";
+import { getStoredToken } from "@/src/lib/auth";
+import type { ClientAddress } from "@/src/types/client_adresses";
-import type { Client } from "@/src/types/client";
-import type { ClientAddress } from "@/src/types/client_adresses";
-import type { ToastNotification } from "@/src/Components/UI";
-import type {
- CreateAddressFormValues,
- UpdateAddressFormValues,
-} from "@/src/validations/client_address.validator";
+export default function AddressDetailsPage() {
+ const params = useParams();
+ const router = useRouter();
-// ─── Page ──────────────────────────────────────────────────────────────────
+ const clientId = params?.clientId as string | undefined;
+ const addressId = params?.addressesId as string | undefined;
-export default function AddressDetailPage() {
- const params = useParams();
- const router = useRouter();
-
- const clientId = (params?.clientId ?? params?.id) as string | undefined;
- const addressId = params?.addressId as string | undefined;
-
- // ── Data state ───────────────────────────────────────────────────────────
const [address, setAddress] = useState(null);
- const [client, setClient] = useState(null);
const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
+ const [error, setError] = useState(null);
- // ── Modal state ──────────────────────────────────────────────────────────
- const [editOpen, setEditOpen] = useState(false);
- const [deleteOpen, setDeleteOpen] = useState(false);
- const [deleting, setDeleting] = useState(false);
-
- // ── Toast ────────────────────────────────────────────────────────────────
- const [notification, setNotification] = useState(null);
-
- const notify = (n: ToastNotification) => {
- setNotification(n);
- setTimeout(() => setNotification(null), 4000);
- };
-
- // ── Fetch ────────────────────────────────────────────────────────────────
- useEffect(() => {
+ const loadAddress = useCallback(async () => {
if (!clientId || !addressId) return;
setLoading(true);
-
- Promise.all([
- clientAddressService.getById(clientId, addressId, getStoredToken()),
- clientService.getById(clientId, getStoredToken()),
- ])
- .then(([addrRes, clientRes]) => {
- const raw = (addrRes as any).data ?? addrRes;
- setAddress({ ...raw, id: raw.id ?? raw._id });
- setClient(clientRes?.data);
- })
- .catch(() => setError("تعذّر تحميل بيانات العنوان. يرجى المحاولة مجدداً."))
- .finally(() => setLoading(false));
+ setError(null);
+ try {
+ const token = getStoredToken();
+ const res = await clientAddressService.getById(clientId, addressId, token);
+ // normalize in case the API returns _id instead of id
+ const raw = res.data as ClientAddress & { _id?: string };
+ setAddress({ ...raw, id: raw.id ?? raw._id ?? "" });
+ } catch {
+ setError("تعذّر تحميل بيانات العنوان. يرجى المحاولة لاحقاً.");
+ } finally {
+ setLoading(false);
+ }
}, [clientId, addressId]);
- // ── Update ───────────────────────────────────────────────────────────────
- const handleUpdateSubmit = async (
- data: CreateAddressFormValues | UpdateAddressFormValues
- ): Promise => {
- if (!clientId || !addressId) return false;
- try {
- const res = await clientAddressService.update(
- clientId,
- addressId,
- data as UpdateAddressFormValues,
- getStoredToken()
- );
- const raw = (res as any).data ?? res;
- setAddress({ ...raw, id: raw.id ?? raw._id });
- notify({ type: "success", message: "تم تحديث العنوان بنجاح." });
- return true;
- } catch (err) {
- notify({
- type: "error",
- message: err instanceof Error ? err.message : "تعذّر تحديث العنوان.",
- });
- return false;
- }
- };
+ useEffect(() => {
+ loadAddress();
+ }, [loadAddress]);
- // ── Delete ───────────────────────────────────────────────────────────────
- const handleDeleteConfirm = async () => {
- if (!clientId || !addressId || deleting) return;
- setDeleting(true);
- try {
- await clientAddressService.delete(clientId, addressId, getStoredToken());
- notify({ type: "success", message: "تم حذف العنوان بنجاح." });
- // short delay so the toast shows before navigation
- setTimeout(() => {
- router.replace(`/dashboard/clients/${clientId}/addresses`);
- }, 700);
- } catch (err) {
- notify({
- type: "error",
- message: err instanceof Error ? err.message : "تعذّر حذف العنوان.",
- });
- setDeleting(false);
- }
- };
-
- // ── Guards ───────────────────────────────────────────────────────────────
+ // ── Loading ──────────────────────────────────────────────────────────────
if (loading) {
return (
-
-
+
+
+ جارٍ تحميل بيانات العنوان…
);
}
+ // ── Error ────────────────────────────────────────────────────────────────
if (error || !address) {
return (
-
-
-
+ العودة إلى العناوين
+
+
);
}
- // ── Render ────────────────────────────────────────────────────────────────
+ // ── Content ──────────────────────────────────────────────────────────────
return (
- <>
-
+
+ router.push(`/dashboard/${clientId}/addresses`)}
+ style={{
+ display: "inline-flex",
+ alignItems: "center",
+ gap: 6,
+ fontSize: 12,
+ fontWeight: 600,
+ color: "var(--color-text-muted)",
+ background: "none",
+ border: "none",
+ cursor: "pointer",
+ alignSelf: "flex-start",
+ fontFamily: "var(--font-sans)",
+ }}
+ >
+
+ كل العناوين
+
- {/* Edit modal */}
- {editOpen && (
- setEditOpen(false)}
- onSubmit={handleUpdateSubmit}
- />
- )}
-
- {/* Delete confirmation */}
- { if (!deleting) setDeleteOpen(false); }}
- onConfirm={handleDeleteConfirm}
- title="حذف العميل"
- description={`هل أنت متأكد من حذف ${address.label}؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء.`}
- />
-
-
-
- {/* ── Page header ── */}
-
-
- {/* ── 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 4021d1e..8bf5fe1 100644
--- a/app/dashboard/clients/[clientId]/addresses/page.tsx
+++ b/app/dashboard/clients/[clientId]/addresses/page.tsx
@@ -3,14 +3,19 @@
import { useCallback, useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
-import { Alert, ConfirmDialog, 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";
-import { getStoredToken } from "@/src/lib/auth";
+import { clientService } from "@/src/services/client.service";
+import { getStoredToken } from "@/src/lib/auth";
import type { Client, ClientFormData } from "@/src/types/client";
-import type { ClientAddress } from "@/src/types/client_adresses";
+import type { ClientAddress } from "@/src/types/client_adresses";
import { AddressFormModal, ClientFormModal } from "@/src/Components/Client";
import { ArchivedClientsAddressesModal } from "@/src/Components/Client_Adress/archive/ArchivedClientsAddressesModal";
@@ -23,16 +28,16 @@ import type {
function labelIcon(label: string): string {
const map: Record = {
- "فوترة": "💳",
- "شحن": "📦",
- "المقر الرئيسي": "🏢",
- "فرع": "🏬",
- "مستودع": "🏭",
- billing: "💳",
- shipping: "📦",
- "head office": "🏢",
- branch: "🏬",
- warehouse: "🏭",
+ فوترة: "💳",
+ شحن: "📦",
+ "المقر الرئيسي": "🏢",
+ فرع: "🏬",
+ مستودع: "🏭",
+ billing: "💳",
+ shipping: "📦",
+ "head office": "🏢",
+ branch: "🏬",
+ warehouse: "🏭",
};
return map[label.toLowerCase()] ?? "📍";
}
@@ -40,14 +45,21 @@ function labelIcon(label: string): string {
// ─── AddressCard ───────────────────────────────────────────────────────────
interface AddressCardProps {
- address: ClientAddress;
- onEdit: () => void;
- onDelete: () => void;
- onSetPrimary: () => void;
+ address: ClientAddress;
+ onView: () => void;
+ onEdit: () => void;
+ onDelete: () => void;
+ onSetPrimary: () => void;
settingPrimary: boolean; // true only while THIS card's request is in flight
}
-function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }: AddressCardProps) {
+function AddressCard({
+ address,
+ onEdit,
+ onDelete,
+ onSetPrimary,
+ settingPrimary,
+}: AddressCardProps) {
const { details, contactPerson } = address;
const {
street,
@@ -73,6 +85,7 @@ function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }
padding: "1.25rem",
background: "var(--color-surface)",
boxShadow: "var(--shadow-card)",
+ cursor: "pointer",
}}
>
{/* Label row */}
@@ -136,7 +149,13 @@ function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }
gap: 2,
}}
>
-
+
{street}
@@ -149,7 +168,14 @@ function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }
{(buildingNo || unitNo || additionalNo) && (
-
+
{buildingNo && `مبنى ${buildingNo}`}
{unitNo && ` · وحدة ${unitNo}`}
{additionalNo && ` · رقم إضافي ${additionalNo}`}
@@ -185,6 +211,7 @@ function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }
{/* Actions */}
e.stopPropagation()}
style={{
display: "flex",
flexWrap: "wrap",
@@ -194,7 +221,12 @@ function AddressCard({ address, onEdit, onDelete, onSetPrimary, settingPrimary }
borderTop: "1px solid var(--color-border)",
}}
>
-
+
تعديل
@@ -233,13 +265,13 @@ function ActionBtn({
disabled,
children,
}: {
- onClick: () => void;
- color: string;
- bg: string;
- border: string;
- style?: React.CSSProperties;
+ onClick: () => void;
+ color: string;
+ bg: string;
+ border: string;
+ style?: React.CSSProperties;
disabled?: boolean;
- children: React.ReactNode;
+ children: React.ReactNode;
}) {
return (
void;
+ client: Client;
+ onClose: () => void;
onSubmit: (data: ClientFormData, isNew: boolean) => Promise;
}
@@ -281,7 +313,9 @@ function ClientEditModal({ client, onClose, onSubmit }: ClientEditModalProps) {
role="dialog"
aria-modal="true"
aria-label="تعديل بيانات العميل"
- onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
+ onClick={(e) => {
+ if (e.target === e.currentTarget) onClose();
+ }}
style={{
position: "fixed",
inset: 0,
@@ -320,8 +354,8 @@ function ClientEditModal({ client, onClose, onSubmit }: ClientEditModalProps) {
// ─── Page ──────────────────────────────────────────────────────────────────
export default function ClientAddressesPage() {
- const params = useParams();
- const router = useRouter();
+ const params = useParams();
+ const router = useRouter();
const clientId = (params?.clientId ?? params?.id) as string | undefined;
@@ -332,7 +366,7 @@ export default function ClientAddressesPage() {
}, [clientId]);
// ── Parent client ────────────────────────────────────────────────────────
- const [client, setClient] = useState(null);
+ const [client, setClient] = useState(null);
const [clientLoading, setClientLoading] = useState(true);
const loadClient = useCallback(() => {
@@ -341,7 +375,7 @@ export default function ClientAddressesPage() {
setClientLoading(true);
clientService
.getById(clientId, getStoredToken())
- .then((res) => setClient(res.data))
+ .then((res) => setClient(res))
.catch(() => router.replace("/dashboard/clients"))
.finally(() => setClientLoading(false));
});
@@ -366,18 +400,21 @@ export default function ClientAddressesPage() {
} = useClientAddresses(clientId ?? "");
// ── Modal state ──────────────────────────────────────────────────────────
- const [addrFormTarget, setAddrFormTarget] = useState(false);
- const [deleteTarget, setDeleteTarget] = useState(null);
- const [deleting, setDeleting] = useState(false);
- const [editingClient, setEditingClient] = useState(false);
+ const [addrFormTarget, setAddrFormTarget] = useState<
+ ClientAddress | null | false
+ >(false);
+ const [deleteTarget, setDeleteTarget] = useState(null);
+ const [deleting, setDeleting] = useState(false);
+ const [editingClient, setEditingClient] = useState(false);
// Archive browser modal open/closed — scoped to this client's addresses
- const [archiveOpen, setArchiveOpen] = useState(false);
+ const [archiveOpen, setArchiveOpen] = useState(false);
// ── Address form handler ─────────────────────────────────────────────────
const handleAddressSubmit = async (
- data: CreateAddressFormValues | UpdateAddressFormValues
+ data: CreateAddressFormValues | UpdateAddressFormValues,
): Promise => {
- if (addrFormTarget === null) return createAddress(data as CreateAddressFormValues);
+ if (addrFormTarget === null)
+ return createAddress(data as CreateAddressFormValues);
if (addrFormTarget === false) return false;
return updateAddress(addrFormTarget.id, data as UpdateAddressFormValues);
};
@@ -403,7 +440,7 @@ export default function ClientAddressesPage() {
if (!client) return false;
try {
const res = await clientService.update(client.id, data, getStoredToken());
- setClient(res.data);
+ setClient(res);
return true;
} catch {
return false;
@@ -413,7 +450,14 @@ export default function ClientAddressesPage() {
// ── Loading guard ────────────────────────────────────────────────────────
if (!clientId || clientLoading) {
return (
-
+
{ if (!deleting) setDeleteTarget(null); }}
+ onCancel={() => {
+ if (!deleting) setDeleteTarget(null);
+ }}
onConfirm={handleDeleteConfirm}
title="حذف العميل"
description={`هل أنت متأكد من حذف ${deleteTarget?.label ?? ""}؟ سيتم حذف جميع عناوينه أيضاً. لا يمكن التراجع عن هذا الإجراء.`}
@@ -469,8 +515,9 @@ export default function ClientAddressesPage() {
/>
)}
-
-
+
{/* ── Header ── */}
-