Merge pm (panel-based access, supervisor role) onto the restored RBAC base

Reverting 253c922 first made this merge small — only two conflicts:
- MobileTabBar: supervisor null-check combined with v7's /review hiding
- ArrivalsPage: pm's PM+supervisor scoping combined with v8's 3-way fetch
  (transfers) and its transfer-receive helpers, plus pm's canAccess gate
This commit is contained in:
MMD
2026-10-01 15:36:57 +03:30
41 changed files with 743 additions and 146 deletions
+19 -2
View File
@@ -36,6 +36,7 @@ import {
DialogHeader,
DialogTitle,
} from "../../components/ui/dialog";
import { useAuthStore } from "../../store/auth";
const TOTAL_STEPS = 5;
@@ -149,6 +150,17 @@ export function WizardPage({ isStandalone = false }: { isStandalone?: boolean })
const suppliers = useSuppliers();
const materials = useMaterials();
const projects = useProjects();
// سطح limited تدارکات: استعلام فقط برای پروژه‌های عضویت — بدون افزودن پروژه جدید
const user = useAuthStore((s) => s.user);
const isLimitedProcurement =
user?.panels?.procurement === "limited" && user?.role !== "admin";
const memberProjectNames = useMemo(
() =>
projects
.filter((p) => (user?.member_project_ids ?? []).includes(p.id))
.map((p) => p.name),
[projects, user],
);
const topics = useAppStore((s) => s.topics);
const createQuote = useCreateQuote();
const addSupplier = useAddSupplier();
@@ -504,13 +516,17 @@ export function WizardPage({ isStandalone = false }: { isStandalone?: boolean })
// ---------- پروژه‌ها ----------
const projectNames = useMemo(() => {
const names = new Set<string>(projects.map((p) => p.name));
const names = new Set<string>(
isLimitedProcurement
? memberProjectNames
: projects.map((p) => p.name),
);
quotes.forEach((q) => {
if (q.project) names.add(q.project);
(q.projects || []).forEach((n) => names.add(n));
});
return [...names];
}, [projects, quotes]);
}, [projects, quotes, isLimitedProcurement, memberProjectNames]);
const assign = (tempId: string, proj: string) => {
setAssignment((a) => {
@@ -950,6 +966,7 @@ export function WizardPage({ isStandalone = false }: { isStandalone?: boolean })
assignment={assignment}
onAssign={assign}
onUnassign={unassign}
canAddProject={!isLimitedProcurement}
onAddProject={(name) => {
void addProject.mutateAsync({ id: nextId("p", projects), name });
}}