feat(settings): add security center, 2FA/TOTP, avatar upload, system settings
- Add TOTP implementation and two-factor data-access for 2FA enrollment - Add security center card with password policy and session management - Add avatar upload action and component - Add system settings actions and data-access (actions-system-settings, data-access-system-settings) - Add notification preferences and service actions - Add security-utils and student-overview-data with tests - Update existing settings views, data-access, and types for new features
This commit is contained in:
@@ -12,6 +12,7 @@ import { ProfileSettingsForm } from "@/modules/settings/components/profile-setti
|
||||
import { PasswordChangeForm } from "@/modules/settings/components/password-change-form"
|
||||
import { NotificationPreferencesForm } from "@/modules/settings/components/notification-preferences-form"
|
||||
import { AiProviderSettingsCard } from "@/modules/settings/components/ai-provider-settings-card"
|
||||
import { SecurityCenterCard } from "@/modules/settings/components/security-center-card"
|
||||
import { SettingsSectionErrorBoundary } from "@/modules/settings/components/settings-section-error-boundary"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
@@ -44,6 +45,8 @@ interface SettingsViewProps {
|
||||
notificationPreferences: NotificationPreferences
|
||||
/** General 标签页中 ProfileSettingsForm 下方的内容(角色专属快捷链接等) */
|
||||
generalExtra?: ReactNode
|
||||
/** 当前请求的 User-Agent,用于安全中心标记当前会话 */
|
||||
currentUserAgent?: string
|
||||
}
|
||||
|
||||
const VALID_TABS = ["general", "notifications", "appearance", "security", "ai"] as const
|
||||
@@ -87,6 +90,7 @@ function SettingsViewInner({
|
||||
user,
|
||||
notificationPreferences,
|
||||
generalExtra,
|
||||
currentUserAgent,
|
||||
}: SettingsViewProps) {
|
||||
const t = useTranslations("settings")
|
||||
const router = useRouter()
|
||||
@@ -94,7 +98,15 @@ function SettingsViewInner({
|
||||
const { hasPermission } = usePermission()
|
||||
|
||||
const tabParam = searchParams.get("tab")
|
||||
const activeTab: TabValue = isTabValue(tabParam) ? tabParam : "general"
|
||||
const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
|
||||
|
||||
// 解析 tab 参数,对无权限的 tab(如非管理员的 ai)回退到 general
|
||||
function resolveTab(value: string | null): TabValue {
|
||||
if (!isTabValue(value)) return "general"
|
||||
if (value === "ai" && !canConfigureAi) return "general"
|
||||
return value
|
||||
}
|
||||
const activeTab: TabValue = resolveTab(tabParam)
|
||||
|
||||
const handleTabChange = (value: string) => {
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
@@ -107,8 +119,6 @@ function SettingsViewInner({
|
||||
router.push(query ? `?${query}` : "?", { scroll: false })
|
||||
}
|
||||
|
||||
const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col gap-8 p-8">
|
||||
<div className="flex flex-col justify-between gap-4 md:flex-row md:items-center">
|
||||
@@ -178,6 +188,7 @@ function SettingsViewInner({
|
||||
<SettingsSectionErrorBoundary>
|
||||
<Suspense fallback={<SettingsSectionSkeleton />}>
|
||||
<PasswordChangeForm />
|
||||
<SecurityCenterCard currentDeviceLabel={currentUserAgent} />
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t("security.session.title")}</CardTitle>
|
||||
|
||||
Reference in New Issue
Block a user