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.""" volume — the RBAC model expects these and login 500s without them."""
stmts = ( stmts = (
"ALTER TABLE users ADD COLUMN IF NOT EXISTS username TEXT", "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 location TEXT DEFAULT ''",
"ALTER TABLE projects ADD COLUMN IF NOT EXISTS project_manager 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", "ALTER TABLE projects ADD COLUMN IF NOT EXISTS planned_start DATE",
@@ -212,6 +212,9 @@ export function OrganizationPage() {
{users.map((u) => { {users.map((u) => {
const isAdmin = u.role === "admin"; const isAdmin = u.role === "admin";
const hasPanel = Boolean(u.panels?.procurement || u.panels?.inventory || u.panels?.pm); const hasPanel = Boolean(u.panels?.procurement || u.panels?.inventory || u.panels?.pm);
// سرپرست/مدیر پروژه بدون پنل هم عضویت پروژهای میخواهد —
// ورود سرپرست بدون پروژه مسدود است، پس ویرایشگر باید باز باشد
const canAssignProjects = hasPanel || u.role === "supervisor" || u.role === "pm";
return ( return (
<tr key={u.id} className="border-b border-line/50 transition-colors hover:bg-canvas/30"> <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"> <td className="py-2.5">
{isAdmin ? ( {isAdmin ? (
<span className="text-[11px] text-muted">دسترسی خودکار به تمام پروژه‌ها</span> <span className="text-[11px] text-muted">دسترسی خودکار به تمام پروژه‌ها</span>
) : hasPanel ? ( ) : canAssignProjects ? (
<div className="w-72"> <div className="w-72">
<ProjectMultiSelect <ProjectMultiSelect
options={projects.map((p) => ({ id: p.id, name: p.name }))} options={projects.map((p) => ({ id: p.id, name: p.name }))}