Author SHA1 Message Date
MMD 707e40e840 fix(org): show the project-membership editor for supervisor/pm roles without panels
Supervisors are scoped by project membership (and are blocked at login
without one), but the editor was hidden unless the user had a panel —
a coordinator could never be given a project. Backend always allowed it.
2026-10-08 11:13:53 +03:30
MMD ca2c1bb8ce fix(db): add missing 'supervisor' value to the user_role enum at startup
CREATE TYPE ... EXCEPTION WHEN duplicate_object no-ops on existing types, so
volumes created before 'supervisor' existed kept an enum without it and
assigning the role 500'd: invalid input value for enum user_role: "supervisor".
ALTER TYPE ... ADD VALUE IF NOT EXISTS is idempotent and safe on any volume.
2026-10-08 11:04:39 +03:30
2 changed files with 7 additions and 1 deletions
+3
View File
@@ -420,6 +420,9 @@ async def ensure_rbac_schema(engine: AsyncEngine) -> None:
volume — the RBAC model expects these and login 500s without them."""
stmts = (
"ALTER TABLE users ADD COLUMN IF NOT EXISTS username TEXT",
# The live user_role enum predates 'supervisor' — CREATE TYPE no-ops on
# an existing type, so without this ALTER, assigning the role 500s.
"ALTER TYPE user_role ADD VALUE IF NOT EXISTS 'supervisor'",
"ALTER TABLE projects ADD COLUMN IF NOT EXISTS location TEXT DEFAULT ''",
"ALTER TABLE projects ADD COLUMN IF NOT EXISTS project_manager TEXT DEFAULT ''",
"ALTER TABLE projects ADD COLUMN IF NOT EXISTS planned_start DATE",
@@ -212,6 +212,9 @@ export function OrganizationPage() {
{users.map((u) => {
const isAdmin = u.role === "admin";
const hasPanel = Boolean(u.panels?.procurement || u.panels?.inventory || u.panels?.pm);
// سرپرست/مدیر پروژه بدون پنل هم عضویت پروژهای میخواهد —
// ورود سرپرست بدون پروژه مسدود است، پس ویرایشگر باید باز باشد
const canAssignProjects = hasPanel || u.role === "supervisor" || u.role === "pm";
return (
<tr key={u.id} className="border-b border-line/50 transition-colors hover:bg-canvas/30">
@@ -320,7 +323,7 @@ export function OrganizationPage() {
<td className="py-2.5">
{isAdmin ? (
<span className="text-[11px] text-muted">دسترسی خودکار به تمام پروژه‌ها</span>
) : hasPanel ? (
) : canAssignProjects ? (
<div className="w-72">
<ProjectMultiSelect
options={projects.map((p) => ({ id: p.id, name: p.name }))}