"use client"; import { useCallback, useEffect, useReducer, useRef, useState } from "react"; import { Alert, Spinner } from "@/src/Components/UI"; import { getStoredToken } from "@/src/lib/auth"; import { get, post, put, del } from "@/src/services/api"; // ── Types ────────────────────────────────────────────────────────────────── interface Branch { id: string; name: string; address?: string | null; phone?: string | null; createdAt: string; updatedAt: string; } interface BranchFormData { name: string; address: string; phone: string; } // ── State / Reducer ──────────────────────────────────────────────────────── interface State { branches: Branch[]; loading: boolean; total: number; pages: number; error: string | null; } type Action = | { type: "LOAD_START" } | { type: "LOAD_OK"; branches: Branch[]; total: number; pages: number } | { type: "LOAD_ERR"; error: string } | { type: "ADD"; branch: Branch } | { type: "UPDATE"; branch: Branch } | { type: "DELETE"; id: string } | { type: "CLEAR_ERR" }; function reducer(s: State, a: Action): State { switch (a.type) { case "LOAD_START": return { ...s, loading: true, error: null }; case "LOAD_OK": return { ...s, loading: false, branches: a.branches, total: a.total, pages: a.pages }; case "LOAD_ERR": return { ...s, loading: false, error: a.error }; case "ADD": return { ...s, branches: [a.branch, ...s.branches], total: s.total + 1 }; case "UPDATE": return { ...s, branches: s.branches.map(b => b.id === a.branch.id ? a.branch : b) }; case "DELETE": return { ...s, branches: s.branches.filter(b => b.id !== a.id), total: Math.max(0, s.total - 1) }; case "CLEAR_ERR": return { ...s, error: null }; default: return s; } } // ── Branch Form Modal ────────────────────────────────────────────────────── function BranchFormModal({ editBranch, onClose, onSubmit, }: { editBranch: Branch | null; onClose: () => void; onSubmit: (data: BranchFormData, isNew: boolean) => Promise; }) { const isNew = editBranch === null; const [form, setForm] = useState({ name: editBranch?.name ?? "", address: editBranch?.address ?? "", phone: editBranch?.phone ?? "", }); const [errors, setErrors] = useState>({}); const [saving, setSaving] = useState(false); const [apiErr, setApiErr] = useState(""); const nameRef = useRef(null); useEffect(() => { nameRef.current?.focus(); }, []); useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h); }, [onClose]); function validate(): boolean { const e: Partial = {}; if (!form.name.trim()) e.name = "اسم الفرع مطلوب"; setErrors(e); return Object.keys(e).length === 0; } async function handleSubmit(ev: React.FormEvent) { ev.preventDefault(); if (!validate()) return; setSaving(true); setApiErr(""); const ok = await onSubmit(form, isNew); setSaving(false); if (ok) onClose(); else setApiErr("حدث خطأ غير متوقع. يرجى المحاولة لاحقاً."); } const inputBase: React.CSSProperties = { width: "100%", height: 40, padding: "0 0.75rem", borderRadius: "var(--radius-md)", border: "1px solid var(--color-border)", background: "var(--color-surface)", fontSize: 13, color: "var(--color-text-primary)", outline: "none", fontFamily: "var(--font-sans)", }; return (
{ if (e.target === e.currentTarget) onClose(); }} style={{ position: "fixed", inset: 0, zIndex: 50, background: "rgba(15,23,42,0.55)", backdropFilter: "blur(4px)", display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem", }} >
e.stopPropagation()} style={{ width: "100%", maxWidth: 480, background: "var(--color-surface)", borderRadius: "var(--radius-2xl)", border: "1px solid var(--color-border)", boxShadow: "0 24px 64px rgba(0,0,0,.18)", overflow: "hidden", }}>

{isNew ? "إضافة فرع" : "تعديل فرع"}

{isNew ? "فرع جديد" : editBranch?.name}

{apiErr && setApiErr("")} />}
); } // ── Delete Modal ─────────────────────────────────────────────────────────── function BranchDeleteModal({ branch, deleting, onCancel, onConfirm }: { branch: Branch; deleting: boolean; onCancel: () => void; onConfirm: () => void; }) { 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", }}>

حذف الفرع

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

); } // ── Toast ────────────────────────────────────────────────────────────────── function Toast({ type, message }: { type: "success" | "error"; message: string }) { const ok = type === "success"; return (
{ok ? "✓" : "⚠"} {message}
); } // ── Page ─────────────────────────────────────────────────────────────────── export default function BranchesPage() { const [state, dispatch] = useReducer(reducer, { branches: [], loading: true, total: 0, pages: 1, error: null, }); const [search, setSearch] = useState(""); const [page, setPage] = useState(1); const [formTarget, setFormTarget] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); const [notification, setNotification] = useState<{ type: "success" | "error"; message: string } | null>(null); const notify = useCallback((n: { type: "success" | "error"; message: string }) => { setNotification(n); setTimeout(() => setNotification(null), 4000); }, []); const loadBranches = useCallback(async (p: number, q: string) => { dispatch({ type: "LOAD_START" }); try { const token = getStoredToken(); const qs = `?page=${p}&limit=12${q ? `&search=${encodeURIComponent(q)}` : ""}`; const res = await get<{ data: { data: Branch[]; meta?: { total: number; pages: number }; pagination?: { total: number; pages: number } } }>(`branches${qs}`, token); const payload = (res as unknown as { data: { data: Branch[]; meta?: { total: number; pages: number }; pagination?: { total: number; pages: number } } }).data; dispatch({ type: "LOAD_OK", branches: payload.data ?? [], total: payload.meta?.total ?? payload.pagination?.total ?? 0, pages: payload.meta?.pages ?? payload.pagination?.pages ?? 1, }); } catch { dispatch({ type: "LOAD_ERR", error: "تعذّر تحميل الفروع. يرجى المحاولة مجدداً." }); } }, []); useEffect(() => { loadBranches(page, search); }, [page, search, loadBranches]); const handleFormSubmit = useCallback(async (data: BranchFormData, isNew: boolean): Promise => { try { const token = getStoredToken(); if (isNew) { const res = await post<{ data: Branch }>("branches", data, token); dispatch({ type: "ADD", branch: (res as unknown as { data: Branch }).data }); notify({ type: "success", message: "تم إضافة الفرع بنجاح." }); } else { const res = await put<{ data: Branch }>(`branches/${(formTarget as Branch).id}`, data, token); dispatch({ type: "UPDATE", branch: (res as unknown as { data: Branch }).data }); notify({ type: "success", message: "تم تحديث الفرع بنجاح." }); } return true; } catch (err) { notify({ type: "error", message: err instanceof Error ? err.message : "تعذّر حفظ الفرع." }); return false; } }, [formTarget, notify]); const handleDeleteConfirm = useCallback(async () => { if (!deleteTarget) return; setDeleting(true); try { const token = getStoredToken(); await del(`branches/${deleteTarget.id}`, token); dispatch({ type: "DELETE", id: deleteTarget.id }); notify({ type: "success", message: "تم حذف الفرع بنجاح." }); setDeleteTarget(null); } catch (err) { notify({ type: "error", message: err instanceof Error ? err.message : "تعذّر حذف الفرع." }); } finally { setDeleting(false); } }, [deleteTarget, notify]); const cardStyle: React.CSSProperties = { borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border)", background: "var(--color-surface)", overflow: "hidden", boxShadow: "var(--shadow-card)", }; const thStyle: React.CSSProperties = { padding: "0.75rem 1.5rem", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.2em", color: "var(--color-text-muted)", background: "var(--color-surface-muted)", borderBottom: "1px solid var(--color-border)", }; return ( <> {notification && } {formTarget !== false && ( setFormTarget(false)} onSubmit={handleFormSubmit} /> )} {deleteTarget && ( setDeleteTarget(null)} onConfirm={handleDeleteConfirm} /> )}
{/* Header */}

إدارة الفروع

الفروع

إجمالي {state.total} فرع

{ setSearch(e.target.value); setPage(1); }} dir="rtl" style={{ width: "100%", height: 40, paddingRight: 36, paddingLeft: 12, borderRadius: "var(--radius-lg)", border: "1px solid var(--color-border)", background: "var(--color-surface)", fontSize: 13, color: "var(--color-text-primary)", outline: "none", fontFamily: "var(--font-sans)" }} />
{state.error && dispatch({ type: "CLEAR_ERR" })} />} {/* Table */}
الفرع العنوان الهاتف تاريخ الإنشاء إجراءات
{state.loading ? (
جارٍ التحميل…
) : state.branches.length === 0 ? (

{search ? `لا توجد نتائج لـ "${search}"` : "لا توجد فروع لعرضها."}

{!search && ( )}
) : (
    {state.branches.map((b, i) => (
  • {b.name}

    {b.id.slice(0, 8)}…

    {b.address ?? "—"} {b.phone ?? "—"} {new Date(b.createdAt).toLocaleDateString("ar-SA", { year: "numeric", month: "short", day: "numeric" })}
  • ))}
)} {state.pages > 1 && (
صفحة {page} من {state.pages}
{[ { label: "السابق", action: () => setPage(p => Math.max(1, p - 1)), disabled: page === 1 }, { label: "التالي", action: () => setPage(p => Math.min(state.pages, p + 1)), disabled: page === state.pages }, ].map(btn => ( ))}
)}
); }