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
+85
View File
@@ -13,12 +13,14 @@ import { Route as LoginRouteImport } from './routes/login'
import { Route as AuthedRouteImport } from './routes/_authed'
import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
import { Route as AuthedSidRouteImport } from './routes/_authed/sid'
import { Route as AuthedRolesRouteImport } from './routes/_authed/roles'
import { Route as AuthedRobotsRouteImport } from './routes/_authed/robots'
import { Route as AuthedLocalesRouteImport } from './routes/_authed/locales'
import { Route as AuthedEventReportsRouteImport } from './routes/_authed/event-reports'
import { Route as AuthedDimzouTranslationsRouteImport } from './routes/_authed/dimzou-translations'
import { Route as AuthedDevicesRouteImport } from './routes/_authed/devices'
import { Route as AuthedCommentReportsRouteImport } from './routes/_authed/comment-reports'
import { Route as AuthedAdminUsersRouteImport } from './routes/_authed/admin-users'
import { Route as AuthedCategoriesIndexRouteImport } from './routes/_authed/categories/index'
import { Route as AuthedStudioSubtopicsRouteImport } from './routes/_authed/studio/subtopics'
import { Route as AuthedStudioStylePresetsRouteImport } from './routes/_authed/studio/style-presets'
@@ -31,6 +33,8 @@ import { Route as AuthedExcProvidersRouteImport } from './routes/_authed/exc/pro
import { Route as AuthedExcOrdersRouteImport } from './routes/_authed/exc/orders'
import { Route as AuthedExcDispatchesRouteImport } from './routes/_authed/exc/dispatches'
import { Route as AuthedExcDemandsRouteImport } from './routes/_authed/exc/demands'
import { Route as AuthedDimzouPublicationsRouteImport } from './routes/_authed/dimzou/publications'
import { Route as AuthedDimzouDocumentsRouteImport } from './routes/_authed/dimzou/documents'
import { Route as AuthedCategoriesPendingRouteImport } from './routes/_authed/categories/pending'
const LoginRoute = LoginRouteImport.update({
@@ -52,6 +56,11 @@ const AuthedSidRoute = AuthedSidRouteImport.update({
path: '/sid',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedRolesRoute = AuthedRolesRouteImport.update({
id: '/roles',
path: '/roles',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedRobotsRoute = AuthedRobotsRouteImport.update({
id: '/robots',
path: '/robots',
@@ -83,6 +92,11 @@ const AuthedCommentReportsRoute = AuthedCommentReportsRouteImport.update({
path: '/comment-reports',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedAdminUsersRoute = AuthedAdminUsersRouteImport.update({
id: '/admin-users',
path: '/admin-users',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedCategoriesIndexRoute = AuthedCategoriesIndexRouteImport.update({
id: '/categories/',
path: '/categories/',
@@ -146,6 +160,17 @@ const AuthedExcDemandsRoute = AuthedExcDemandsRouteImport.update({
path: '/exc/demands',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedDimzouPublicationsRoute =
AuthedDimzouPublicationsRouteImport.update({
id: '/dimzou/publications',
path: '/dimzou/publications',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedDimzouDocumentsRoute = AuthedDimzouDocumentsRouteImport.update({
id: '/dimzou/documents',
path: '/dimzou/documents',
getParentRoute: () => AuthedRoute,
} as any)
const AuthedCategoriesPendingRoute = AuthedCategoriesPendingRouteImport.update({
id: '/categories/pending',
path: '/categories/pending',
@@ -155,14 +180,18 @@ const AuthedCategoriesPendingRoute = AuthedCategoriesPendingRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof AuthedIndexRoute
'/login': typeof LoginRoute
'/admin-users': typeof AuthedAdminUsersRoute
'/comment-reports': typeof AuthedCommentReportsRoute
'/devices': typeof AuthedDevicesRoute
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
'/event-reports': typeof AuthedEventReportsRoute
'/locales': typeof AuthedLocalesRoute
'/robots': typeof AuthedRobotsRoute
'/roles': typeof AuthedRolesRoute
'/sid': typeof AuthedSidRoute
'/categories/pending': typeof AuthedCategoriesPendingRoute
'/dimzou/documents': typeof AuthedDimzouDocumentsRoute
'/dimzou/publications': typeof AuthedDimzouPublicationsRoute
'/exc/demands': typeof AuthedExcDemandsRoute
'/exc/dispatches': typeof AuthedExcDispatchesRoute
'/exc/orders': typeof AuthedExcOrdersRoute
@@ -178,15 +207,19 @@ export interface FileRoutesByFullPath {
}
export interface FileRoutesByTo {
'/login': typeof LoginRoute
'/admin-users': typeof AuthedAdminUsersRoute
'/comment-reports': typeof AuthedCommentReportsRoute
'/devices': typeof AuthedDevicesRoute
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
'/event-reports': typeof AuthedEventReportsRoute
'/locales': typeof AuthedLocalesRoute
'/robots': typeof AuthedRobotsRoute
'/roles': typeof AuthedRolesRoute
'/sid': typeof AuthedSidRoute
'/': typeof AuthedIndexRoute
'/categories/pending': typeof AuthedCategoriesPendingRoute
'/dimzou/documents': typeof AuthedDimzouDocumentsRoute
'/dimzou/publications': typeof AuthedDimzouPublicationsRoute
'/exc/demands': typeof AuthedExcDemandsRoute
'/exc/dispatches': typeof AuthedExcDispatchesRoute
'/exc/orders': typeof AuthedExcOrdersRoute
@@ -204,15 +237,19 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport
'/_authed': typeof AuthedRouteWithChildren
'/login': typeof LoginRoute
'/_authed/admin-users': typeof AuthedAdminUsersRoute
'/_authed/comment-reports': typeof AuthedCommentReportsRoute
'/_authed/devices': typeof AuthedDevicesRoute
'/_authed/dimzou-translations': typeof AuthedDimzouTranslationsRoute
'/_authed/event-reports': typeof AuthedEventReportsRoute
'/_authed/locales': typeof AuthedLocalesRoute
'/_authed/robots': typeof AuthedRobotsRoute
'/_authed/roles': typeof AuthedRolesRoute
'/_authed/sid': typeof AuthedSidRoute
'/_authed/': typeof AuthedIndexRoute
'/_authed/categories/pending': typeof AuthedCategoriesPendingRoute
'/_authed/dimzou/documents': typeof AuthedDimzouDocumentsRoute
'/_authed/dimzou/publications': typeof AuthedDimzouPublicationsRoute
'/_authed/exc/demands': typeof AuthedExcDemandsRoute
'/_authed/exc/dispatches': typeof AuthedExcDispatchesRoute
'/_authed/exc/orders': typeof AuthedExcOrdersRoute
@@ -231,14 +268,18 @@ export interface FileRouteTypes {
fullPaths:
| '/'
| '/login'
| '/admin-users'
| '/comment-reports'
| '/devices'
| '/dimzou-translations'
| '/event-reports'
| '/locales'
| '/robots'
| '/roles'
| '/sid'
| '/categories/pending'
| '/dimzou/documents'
| '/dimzou/publications'
| '/exc/demands'
| '/exc/dispatches'
| '/exc/orders'
@@ -254,15 +295,19 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo
to:
| '/login'
| '/admin-users'
| '/comment-reports'
| '/devices'
| '/dimzou-translations'
| '/event-reports'
| '/locales'
| '/robots'
| '/roles'
| '/sid'
| '/'
| '/categories/pending'
| '/dimzou/documents'
| '/dimzou/publications'
| '/exc/demands'
| '/exc/dispatches'
| '/exc/orders'
@@ -279,15 +324,19 @@ export interface FileRouteTypes {
| '__root__'
| '/_authed'
| '/login'
| '/_authed/admin-users'
| '/_authed/comment-reports'
| '/_authed/devices'
| '/_authed/dimzou-translations'
| '/_authed/event-reports'
| '/_authed/locales'
| '/_authed/robots'
| '/_authed/roles'
| '/_authed/sid'
| '/_authed/'
| '/_authed/categories/pending'
| '/_authed/dimzou/documents'
| '/_authed/dimzou/publications'
| '/_authed/exc/demands'
| '/_authed/exc/dispatches'
| '/_authed/exc/orders'
@@ -337,6 +386,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthedSidRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/roles': {
id: '/_authed/roles'
path: '/roles'
fullPath: '/roles'
preLoaderRoute: typeof AuthedRolesRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/robots': {
id: '/_authed/robots'
path: '/robots'
@@ -379,6 +435,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthedCommentReportsRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/admin-users': {
id: '/_authed/admin-users'
path: '/admin-users'
fullPath: '/admin-users'
preLoaderRoute: typeof AuthedAdminUsersRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/categories/': {
id: '/_authed/categories/'
path: '/categories'
@@ -463,6 +526,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthedExcDemandsRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/dimzou/publications': {
id: '/_authed/dimzou/publications'
path: '/dimzou/publications'
fullPath: '/dimzou/publications'
preLoaderRoute: typeof AuthedDimzouPublicationsRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/dimzou/documents': {
id: '/_authed/dimzou/documents'
path: '/dimzou/documents'
fullPath: '/dimzou/documents'
preLoaderRoute: typeof AuthedDimzouDocumentsRouteImport
parentRoute: typeof AuthedRoute
}
'/_authed/categories/pending': {
id: '/_authed/categories/pending'
path: '/categories/pending'
@@ -474,15 +551,19 @@ declare module '@tanstack/react-router' {
}
interface AuthedRouteChildren {
AuthedAdminUsersRoute: typeof AuthedAdminUsersRoute
AuthedCommentReportsRoute: typeof AuthedCommentReportsRoute
AuthedDevicesRoute: typeof AuthedDevicesRoute
AuthedDimzouTranslationsRoute: typeof AuthedDimzouTranslationsRoute
AuthedEventReportsRoute: typeof AuthedEventReportsRoute
AuthedLocalesRoute: typeof AuthedLocalesRoute
AuthedRobotsRoute: typeof AuthedRobotsRoute
AuthedRolesRoute: typeof AuthedRolesRoute
AuthedSidRoute: typeof AuthedSidRoute
AuthedIndexRoute: typeof AuthedIndexRoute
AuthedCategoriesPendingRoute: typeof AuthedCategoriesPendingRoute
AuthedDimzouDocumentsRoute: typeof AuthedDimzouDocumentsRoute
AuthedDimzouPublicationsRoute: typeof AuthedDimzouPublicationsRoute
AuthedExcDemandsRoute: typeof AuthedExcDemandsRoute
AuthedExcDispatchesRoute: typeof AuthedExcDispatchesRoute
AuthedExcOrdersRoute: typeof AuthedExcOrdersRoute
@@ -498,15 +579,19 @@ interface AuthedRouteChildren {
}
const AuthedRouteChildren: AuthedRouteChildren = {
AuthedAdminUsersRoute: AuthedAdminUsersRoute,
AuthedCommentReportsRoute: AuthedCommentReportsRoute,
AuthedDevicesRoute: AuthedDevicesRoute,
AuthedDimzouTranslationsRoute: AuthedDimzouTranslationsRoute,
AuthedEventReportsRoute: AuthedEventReportsRoute,
AuthedLocalesRoute: AuthedLocalesRoute,
AuthedRobotsRoute: AuthedRobotsRoute,
AuthedRolesRoute: AuthedRolesRoute,
AuthedSidRoute: AuthedSidRoute,
AuthedIndexRoute: AuthedIndexRoute,
AuthedCategoriesPendingRoute: AuthedCategoriesPendingRoute,
AuthedDimzouDocumentsRoute: AuthedDimzouDocumentsRoute,
AuthedDimzouPublicationsRoute: AuthedDimzouPublicationsRoute,
AuthedExcDemandsRoute: AuthedExcDemandsRoute,
AuthedExcDispatchesRoute: AuthedExcDispatchesRoute,
AuthedExcOrdersRoute: AuthedExcOrdersRoute,