feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login

This commit is contained in:
ali_mohaghegh
2026-08-25 19:08:15 +03:30
parent 170aed66ea
commit 4e105c5cd5
103 changed files with 16352 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
import { useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { Menu, X } from "lucide-react";
import { cn } from "../lib/utils";
export interface MenuItem {
label?: string;
to?: string;
active?: boolean;
divider?: boolean;
onClick?: () => void;
}
// منوی همبرگری — بالای راست صفحات؛ نمایش پنل فعلی + ناوبری بین پنل‌ها
export function PanelMenu({ items, title }: { items: MenuItem[]; title?: string }) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
const onDoc = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener("mousedown", onDoc);
return () => document.removeEventListener("mousedown", onDoc);
}, [open]);
return (
<div ref={ref} className="relative">
<button
type="button"
onClick={() => setOpen((o) => !o)}
className="inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-lg border border-line bg-panel text-ink hover:bg-slate-bg"
aria-label="منو"
title="منو"
>
{open ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
{open && (
<div className="absolute top-11 right-0 z-50 min-w-56 rounded-xl border border-line bg-panel p-1.5 shadow-lg">
{title && <div className="px-3 py-1.5 text-[11px] font-bold text-muted">{title}</div>}
{items.map((it, i) =>
it.divider ? (
<div key={i} className="my-1 border-t border-line" />
) : it.to ? (
<Link
key={i}
to={it.to}
onClick={() => setOpen(false)}
className={cn(
"flex items-center justify-between gap-3 rounded-lg px-3 py-2 text-[13px] no-underline",
it.active ? "bg-accent font-bold text-white" : "text-ink hover:bg-slate-bg",
)}
>
{it.label}
{it.active && <span className="text-[10px] opacity-80">✓ فعلی</span>}
</Link>
) : (
<button
key={i}
type="button"
onClick={() => {
setOpen(false);
it.onClick?.();
}}
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-start text-[13px] text-ink hover:bg-slate-bg"
>
{it.label}
</button>
),
)}
</div>
)}
</div>
);
}