feat(org,rbac): dedicate organization panel, enforce role permissions, subheader info, and fix supervisor project sync

This commit is contained in:
2026-09-26 18:51:37 +03:30
parent 5a9b543040
commit 387edfb1ab
31 changed files with 921 additions and 643 deletions
+42 -7
View File
@@ -87,7 +87,7 @@ function handle401(path: string, res: Response): void {
// ادمین/مدیر تدارکات بدون پارامتر (همه‌ی پروژه‌ها) باقی می‌ماند.
function activeProjectId(): string | null {
const { user } = useAuthStore.getState();
return user?.role === "pm" ? user.project_id : null;
return user?.role === "supervisor" || user?.role === "pm" ? user.project_id : null;
}
function projectQuery(projectId?: string | null): string {
@@ -323,6 +323,12 @@ interface RawSupplier {
interface RawProject {
id: string;
name: string;
location?: string | null;
project_manager?: string | null;
description?: string | null;
start_date?: string | null;
status?: string | null;
progress?: number | null;
}
interface RawSpec {
attribute_key: string;
@@ -1131,11 +1137,36 @@ export const api = {
projects: {
list: async (): Promise<Project[]> => {
const raw = await getData<RawProject[]>(`/projects?page_size=${PAGE_SIZE}`);
return raw.map((p) => ({ id: p.id, name: p.name }));
return raw.map((p) => ({
id: p.id,
name: p.name,
location: p.location || "",
project_manager: p.project_manager || "",
description: p.description || "",
start_date: p.start_date,
status: p.status || "ACTIVE",
progress: p.progress != null ? Number(p.progress) : 0,
}));
},
create: async (p: Project): Promise<Project> => {
const raw = await postData<RawProject>("/projects", { name: p.name });
return { id: raw.id, name: raw.name };
const raw = await postData<RawProject>("/projects", {
name: p.name,
location: p.location || "",
project_manager: p.project_manager || "",
description: p.description || "",
});
return {
id: raw.id,
name: raw.name,
location: raw.location || "",
project_manager: raw.project_manager || "",
description: raw.description || "",
status: raw.status || "ACTIVE",
progress: raw.progress != null ? Number(raw.progress) : 0,
};
},
delete: async (id: string): Promise<void> => {
await deleteData(`/projects/${id}`);
},
},
@@ -1525,6 +1556,7 @@ interface RawUnitL { id: number; code: string; name_fa: string | null; }
export interface UserRow {
id: string;
casdoor_id: string;
username?: string | null;
first_name: string;
last_name: string;
phone: string | null;
@@ -1553,10 +1585,11 @@ export const apiV26 = {
user: {
id: string;
casdoor_id: string;
username?: string;
first_name: string;
last_name: string;
phone: string | null;
role: "pm" | "procurement_manager" | "admin" | null;
role: "pm" | "procurement_manager" | "admin" | "supervisor" | null;
role_label: string | null;
project_id: string | null;
project_name: string | null;
@@ -1570,6 +1603,7 @@ export const apiV26 = {
expires_in: res.data.expires_in,
user: {
...u,
username: u.username || u.casdoor_id,
display_name: `${u.first_name} ${u.last_name}`.trim() || u.casdoor_id,
},
};
@@ -1589,14 +1623,15 @@ export const apiV26 = {
users: {
me: async (): Promise<import("../store/auth").AuthUser> => {
const data = await getData<{
id: string; casdoor_id: string; first_name: string; last_name: string; phone: string | null;
role: "pm" | "procurement_manager" | "admin" | null; role_label: string | null;
id: string; casdoor_id: string; username?: string; first_name: string; last_name: string; phone: string | null;
role: "pm" | "procurement_manager" | "admin" | "supervisor" | null; role_label: string | null;
project_id: string | null; project_name: string | null;
projects?: { id: string; name: string }[];
}>("/users/me");
return {
id: data.id,
casdoor_id: data.casdoor_id,
username: data.username || data.casdoor_id,
first_name: data.first_name,
last_name: data.last_name,
phone: data.phone,
+6
View File
@@ -31,6 +31,12 @@ export interface Material {
export interface Project {
id: string;
name: string;
location?: string;
project_manager?: string;
description?: string;
start_date?: string | null;
status?: string;
progress?: number;
}
// مرحله‌ی پرداخت