feat(shared): update permissions system, i18n, hooks, and components
- Update permission-bitmap.ts, permissions.ts, resolve-action-error.ts - Update types/permissions.ts - Update rbac i18n messages (en, zh-CN) - Update locale-switcher.tsx - Update use-action-with-toast.ts hook
This commit is contained in:
@@ -4,7 +4,7 @@ import * as React from "react"
|
|||||||
import { useTransition } from "react"
|
import { useTransition } from "react"
|
||||||
import { useLocale, useTranslations } from "next-intl"
|
import { useLocale, useTranslations } from "next-intl"
|
||||||
import { Check, Globe } from "lucide-react"
|
import { Check, Globe } from "lucide-react"
|
||||||
import { toast } from "sonner"
|
import { notify } from "@/shared/lib/notify"
|
||||||
|
|
||||||
import { Button } from "@/shared/components/ui/button"
|
import { Button } from "@/shared/components/ui/button"
|
||||||
import {
|
import {
|
||||||
@@ -35,9 +35,9 @@ export function LocaleSwitcher({ compact = false }: { compact?: boolean }) {
|
|||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
const result = await setLocaleAction(next)
|
const result = await setLocaleAction(next)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(t("switch"))
|
notify.success(t("switch"))
|
||||||
} else {
|
} else {
|
||||||
toast.error(t("switch"))
|
notify.error(t("switch"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useTransition } from "react"
|
import { useTransition } from "react"
|
||||||
import { toast } from "sonner"
|
import { notify } from "@/shared/lib/notify"
|
||||||
import type { ActionState } from "@/shared/types/action-state"
|
import type { ActionState } from "@/shared/types/action-state"
|
||||||
|
|
||||||
export function useActionWithToast<T>() {
|
export function useActionWithToast<T>() {
|
||||||
@@ -11,9 +11,9 @@ export function useActionWithToast<T>() {
|
|||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
const result = await action()
|
const result = await action()
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(result.message || "操作成功")
|
notify.success(result.message || "操作成功")
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || "操作失败")
|
notify.error(result.message || "操作失败")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,11 @@
|
|||||||
},
|
},
|
||||||
"audit": {
|
"audit": {
|
||||||
"read": "Read Audit Logs",
|
"read": "Read Audit Logs",
|
||||||
"readDesc": "Allows viewing system audit logs"
|
"readDesc": "Allows viewing system audit logs",
|
||||||
|
"purge": "Purge Audit Logs",
|
||||||
|
"purgeDesc": "Physically delete expired audit logs (destructive, admin only)",
|
||||||
|
"retentionManage": "Manage Audit Retention",
|
||||||
|
"retentionManageDesc": "Configure audit log retention days and auto-cleanup rules"
|
||||||
},
|
},
|
||||||
"announcement": {
|
"announcement": {
|
||||||
"manage": "Manage Announcements",
|
"manage": "Manage Announcements",
|
||||||
|
|||||||
@@ -173,7 +173,11 @@
|
|||||||
},
|
},
|
||||||
"audit": {
|
"audit": {
|
||||||
"read": "查看审计日志",
|
"read": "查看审计日志",
|
||||||
"readDesc": "允许查看系统审计日志"
|
"readDesc": "允许查看系统审计日志",
|
||||||
|
"purge": "清理审计日志",
|
||||||
|
"purgeDesc": "物理删除过期审计日志(破坏性操作,仅管理员)",
|
||||||
|
"retentionManage": "管理审计保留策略",
|
||||||
|
"retentionManageDesc": "配置审计日志保留天数与自动清理规则"
|
||||||
},
|
},
|
||||||
"announcement": {
|
"announcement": {
|
||||||
"manage": "管理公告",
|
"manage": "管理公告",
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ export const PERMISSION_BITMAP_ORDER: readonly Permission[] = [
|
|||||||
Permissions.SETTINGS_ADMIN,
|
Permissions.SETTINGS_ADMIN,
|
||||||
// Audit
|
// Audit
|
||||||
Permissions.AUDIT_LOG_READ,
|
Permissions.AUDIT_LOG_READ,
|
||||||
|
Permissions.AUDIT_LOG_PURGE,
|
||||||
|
Permissions.AUDIT_RETENTION_MANAGE,
|
||||||
// Announcement
|
// Announcement
|
||||||
Permissions.ANNOUNCEMENT_MANAGE,
|
Permissions.ANNOUNCEMENT_MANAGE,
|
||||||
Permissions.ANNOUNCEMENT_READ,
|
Permissions.ANNOUNCEMENT_READ,
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ export const ROLE_PERMISSIONS_SEED: Record<BuiltinRole, Permission[]> = {
|
|||||||
Permissions.AI_CONFIGURE,
|
Permissions.AI_CONFIGURE,
|
||||||
Permissions.SETTINGS_ADMIN,
|
Permissions.SETTINGS_ADMIN,
|
||||||
Permissions.AUDIT_LOG_READ,
|
Permissions.AUDIT_LOG_READ,
|
||||||
|
Permissions.AUDIT_LOG_PURGE,
|
||||||
|
Permissions.AUDIT_RETENTION_MANAGE,
|
||||||
Permissions.ANNOUNCEMENT_MANAGE,
|
Permissions.ANNOUNCEMENT_MANAGE,
|
||||||
Permissions.ANNOUNCEMENT_READ,
|
Permissions.ANNOUNCEMENT_READ,
|
||||||
Permissions.GRADE_RECORD_MANAGE,
|
Permissions.GRADE_RECORD_MANAGE,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import type { ActionState } from "@/shared/types/action-state"
|
|||||||
* ```tsx
|
* ```tsx
|
||||||
* const t = useTranslations("attendance")
|
* const t = useTranslations("attendance")
|
||||||
* const msg = resolveActionError(result, t, t("errors.unexpected"))
|
* const msg = resolveActionError(result, t, t("errors.unexpected"))
|
||||||
* toast.error(msg)
|
* notify.error(msg)
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function resolveActionError<T>(
|
export function resolveActionError<T>(
|
||||||
|
|||||||
@@ -110,6 +110,10 @@ export const Permissions = {
|
|||||||
|
|
||||||
// Audit
|
// Audit
|
||||||
AUDIT_LOG_READ: "audit_log:read",
|
AUDIT_LOG_READ: "audit_log:read",
|
||||||
|
/** 审计日志清理(物理删除)—— 破坏性操作,仅 admin */
|
||||||
|
AUDIT_LOG_PURGE: "audit_log:purge",
|
||||||
|
/** 审计日志保留策略管理(配置保留天数、自动清理规则) */
|
||||||
|
AUDIT_RETENTION_MANAGE: "audit_retention:manage",
|
||||||
|
|
||||||
// Announcement
|
// Announcement
|
||||||
ANNOUNCEMENT_MANAGE: "announcement:manage",
|
ANNOUNCEMENT_MANAGE: "announcement:manage",
|
||||||
@@ -210,6 +214,17 @@ export const Permissions = {
|
|||||||
|
|
||||||
export type Permission = (typeof Permissions)[keyof typeof Permissions]
|
export type Permission = (typeof Permissions)[keyof typeof Permissions]
|
||||||
|
|
||||||
|
/** 所有合法的权限值列表(用于运行时校验) */
|
||||||
|
const PERMISSION_VALUES = Object.values(Permissions) as readonly string[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型守卫:判断值是否为合法的 Permission。
|
||||||
|
* 用于从 string[] 安全收窄到 Permission[],替代 `as Permission[]` 断言。
|
||||||
|
*/
|
||||||
|
export function isPermission(value: unknown): value is Permission {
|
||||||
|
return typeof value === "string" && PERMISSION_VALUES.includes(value)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data scope for row-level security.
|
* Data scope for row-level security.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user