Files
NextEdu/src/modules/settings/components/theme-preferences-card.tsx
SpecialX 1fcef5c3aa 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
2026-06-23 17:37:06 +08:00

83 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { Monitor, Moon, Sun, Globe } from "lucide-react"
import { useTheme } from "next-themes"
import { useTranslations } from "next-intl"
import { LocaleSwitcher } from "@/shared/components/locale-switcher"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { Label } from "@/shared/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select"
type ThemeChoice = "system" | "light" | "dark"
/**
* 外观偏好卡片
*
* 包含主题切换system/light/dark和语言切换。
* 语言切换复用 shared/components/locale-switcher 组件。
*/
export function ThemePreferencesCard(): React.ReactElement {
const t = useTranslations("settings.appearance.theme")
const tLang = useTranslations("settings.appearance.language")
const { theme, setTheme } = useTheme()
const value: ThemeChoice = theme === "light" || theme === "dark" || theme === "system" ? theme : "system"
return (
<Card>
<CardHeader>
<CardTitle>{t("title")}</CardTitle>
<CardDescription>{t("description")}</CardDescription>
</CardHeader>
<CardContent className="grid gap-4 sm:max-w-md">
<div className="space-y-2">
<Label htmlFor="theme">{t("label")}</Label>
<Select value={value} onValueChange={(v) => setTheme(v)}>
<SelectTrigger id="theme" suppressHydrationWarning>
<SelectValue placeholder={t("title")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">
<div className="flex items-center gap-2">
<Monitor className="h-4 w-4 text-muted-foreground" />
{t("system")}
</div>
</SelectItem>
<SelectItem value="light">
<div className="flex items-center gap-2">
<Sun className="h-4 w-4 text-muted-foreground" />
{t("light")}
</div>
</SelectItem>
<SelectItem value="dark">
<div className="flex items-center gap-2">
<Moon className="h-4 w-4 text-muted-foreground" />
{t("dark")}
</div>
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="language" className="flex items-center gap-1.5">
<Globe className="h-3.5 w-3.5 text-muted-foreground" />
{tLang("label")}
</Label>
<div id="language">
<LocaleSwitcher />
</div>
<p className="text-xs text-muted-foreground">{tLang("description")}</p>
</div>
</CardContent>
</Card>
)
}