Let admins create and edit roles from the dashboard (Task #82)

What changed:
- Added an `is_system` column to the `roles` table (default 0). The
  built-in roles (admin, user, order_receiver) are flagged as system roles
  in the seed and via a one-off UPDATE on the existing dev DB.
- Created a new `/api/roles` route module with admin-guarded endpoints:
    - POST /api/roles – create a role (validates name format, rejects duplicates).
    - PATCH /api/roles/:id – update bilingual descriptions; name is immutable.
    - DELETE /api/roles/:id – returns 400 for system roles (also defended
      by name allowlist), 409 with userCount/groupCount when in use, 204 on
      success. Cleans up role_permissions in a transaction.
  GET /api/roles was moved out of groups.ts into the new module.
- OpenAPI updated with createRole / updateRole / deleteRole operations,
  isSystem on the Role schema, and CreateRoleBody / UpdateRoleBody /
  RoleDeletionConflict schemas. Regenerated api-zod and api-client-react.
- Added a "Roles" item under the User Management nav group in the admin
  dashboard, plus a new RolesPanel with search, create dialog, edit dialog
  (description-only), and delete dialog with conflict messaging.
- Bilingual translations added in en.json and ar.json (admin.nav.roles +
  admin.roles.*).

Notes / deviations:
- Also marked `order_receiver` as a system role since it is seeded by the
  system and required by the orders feature, even though the task brief
  only called out admin and user.
- Verified end-to-end via the testing skill: list, create, edit, delete,
  duplicate-name validation, and protection of system roles all work.

Replit-Task-Id: b1187555-be09-4687-a9ae-83b123d908bd
This commit is contained in:
riyadhafraa
2026-04-27 10:26:24 +00:00
parent b0b1d673b7
commit 19a20cc5a4
13 changed files with 981 additions and 17 deletions
+3 -2
View File
@@ -23,13 +23,13 @@ async function main() {
// Create roles
const [adminRole] = await db
.insert(rolesTable)
.values({ name: "admin", descriptionAr: "مدير النظام", descriptionEn: "System Administrator" })
.values({ name: "admin", descriptionAr: "مدير النظام", descriptionEn: "System Administrator", isSystem: 1 })
.onConflictDoNothing()
.returning();
const [userRole] = await db
.insert(rolesTable)
.values({ name: "user", descriptionAr: "مستخدم عادي", descriptionEn: "Regular User" })
.values({ name: "user", descriptionAr: "مستخدم عادي", descriptionEn: "Regular User", isSystem: 1 })
.onConflictDoNothing()
.returning();
@@ -56,6 +56,7 @@ async function main() {
name: "order_receiver",
descriptionAr: "مستلم الطلبات",
descriptionEn: "Order Receiver",
isSystem: 1,
})
.onConflictDoNothing()
.returning();