feat(roles): role management pages (super-admin)

- /roles — role table with create dialog and a permission-matrix sheet:
  collapsible per-module sections (selected/total counts, role's own
  modules start expanded) and a key/description filter that force-
  expands matches; super-admin role is read-only
- /admin-users — user search (Enter to run) with role assignment
  dialog; editing your own roles is blocked

Both nav items gate on auth:role:* keys, which only super-admin holds.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-12 06:42:51 +08:00
co-authored by Claude Fable 5
parent 46056ae4b0
commit ddfec7258f
5 changed files with 768 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
import { api } from '@/api/client'
import type { AdminRole, ApiResponse, PermissionCatalog, RoleUser } from '@/api/types'
const BASE = '/api/admin/v1/auth'
export function fetchRoles() {
return api.get<ApiResponse<AdminRole[]>>(`${BASE}/roles`)
}
export function createRole(name: string) {
return api.post<ApiResponse<AdminRole>>(`${BASE}/roles`, { name })
}
export function syncRolePermissions(role: string, permissions: string[]) {
return api.put<ApiResponse<AdminRole>>(`${BASE}/roles/${role}/permissions`, { permissions })
}
export function deleteRole(role: string) {
return api.delete<ApiResponse<null>>(`${BASE}/roles/${role}`)
}
export function fetchPermissionCatalog() {
return api.get<ApiResponse<PermissionCatalog>>(`${BASE}/permissions`)
}
export function searchRoleUsers(q: string) {
return api.get<ApiResponse<RoleUser[]>>(`${BASE}/users`, { q })
}
export function syncUserRoles(uid: string, roles: string[]) {
return api.put<ApiResponse<RoleUser>>(`${BASE}/users/${uid}/roles`, { roles })
}