@@ -226,19 +252,14 @@ export function OrganizationPage() {
(تغییر نقش فقط توسط ادمین)
)}
-
+
{u.role === "pm" ? (
- ({ id: p.id, name: p.name }))}
+ values={assignedProjectsOf(u).map((p) => p.id)}
+ onChange={(ids) => void syncUserProjects(u, ids)}
disabled={busyId === u.id}
- onChange={(e) => void changeProject(u, e.target.value)}
- >
- — بدون پروژه —
- {data.lookup.projects.map((p) => (
- {p.name}
- ))}
-
+ />
) : (
—
)}
@@ -253,7 +274,9 @@ export function OrganizationPage() {
- «بدون نقش» = حذف مدیر پروژه (از پروژه هم جدا میشود). اتصال به یک پروژه، مدیر قبلی آن پروژه را جابهجا میکند.
+ «بدون نقش» = حذف مدیر پروژه (از همهی پروژهها هم جدا میشود). هر مدیر پروژه میتواند به
+ چند پروژه متصل باشد؛ حذف هر چیپ (×)، فقط همان پروژه را جدا میکند و اتصال به یک پروژه، مدیرِ
+ قبلیِ آن پروژه را جابهجا میکند.
)}
diff --git a/frontend/src/pages/Inventory/useInventoryData.ts b/frontend/src/pages/Inventory/useInventoryData.ts
index 9f7c727..2b2b55a 100644
--- a/frontend/src/pages/Inventory/useInventoryData.ts
+++ b/frontend/src/pages/Inventory/useInventoryData.ts
@@ -54,6 +54,9 @@ export function useInventoryData(): InventoryData {
setWarehouses(whs);
setInventory(inv);
setTransactions(txs);
+ } catch {
+ // پروژهی فعال ممکن است همین حالا باطل شده باشد — گیتِ پروژه در سطح مسیر
+ // (RequirePmProjects) پروژهی جایگزین/صفحهی خالی را نشان میدهد.
} finally {
setLoading(false);
}
diff --git a/frontend/src/store/auth.ts b/frontend/src/store/auth.ts
index 8a9d7c9..c5bc783 100644
--- a/frontend/src/store/auth.ts
+++ b/frontend/src/store/auth.ts
@@ -16,8 +16,9 @@ export interface AuthUser {
phone: string | null;
role: "pm" | "procurement_manager" | "admin" | null;
role_label: string | null;
- project_id: string | null;
+ project_id: string | null; // پروژهی فعال (PM) — با سوییچر عوض میشود
project_name: string | null;
+ projects?: { id: string; name: string }[]; // همهی پروژههای تخصیصیافته (1:N)
display_name: string;
}
@@ -28,6 +29,11 @@ interface AuthStore {
setToken: (token: string | null) => void;
setUser: (user: AuthUser | null) => void;
setDevUser: (role: "pm" | "admin" | "procurement_manager", projectId: string | null, projectName: string | null) => void;
+ // پروژههای تخصیصیافته را مینشاند و پروژهی فعال را اعتبارسنجی میکند:
+ // اگر پروژهی ذخیرهشده (localStorage) باطل شده باشد، به اولین پروژه برمیگردد.
+ setProjects: (projects: { id: string; name: string }[]) => void;
+ // جابهجایی پروژهی فعال بدون هیچ تغییری در نشست/توکن (بدون احراز هویت مجدد)
+ setActiveProject: (projectId: string) => void;
logout: () => void;
}
@@ -53,9 +59,30 @@ export const useAuthStore = create()(
role_label: role === "pm" ? "مدیر پروژه (آزمایشی)" : role === "procurement_manager" ? "مدیر تدارکات (آزمایشی)" : "ادمین (آزمایشی)",
project_id: projectId,
project_name: projectName,
+ projects: projectId ? [{ id: projectId, name: projectName || "" }] : [],
display_name: projectId ? `کاربر آزمایشی — ${projectName || ""}` : "کاربر آزمایشی",
},
}),
+ setProjects: (projects) =>
+ set((s) => {
+ if (!s.user) return {};
+ const active = projects.find((p) => p.id === s.user!.project_id) ?? projects[0] ?? null;
+ return {
+ user: {
+ ...s.user,
+ projects,
+ project_id: active?.id ?? null,
+ project_name: active?.name ?? null,
+ },
+ };
+ }),
+ setActiveProject: (projectId) =>
+ set((s) => {
+ if (!s.user) return {};
+ const target = (s.user.projects ?? []).find((p) => p.id === projectId);
+ if (!target) return {};
+ return { user: { ...s.user, project_id: target.id, project_name: target.name } };
+ }),
logout: () => set({ token: null, user: null, isDev: false }),
}),
{ name: "procurement-auth-v1" },