release: prepare v1.2.42
Docker Release / Check web and api (push) Waiting to run
Docker Release / Resolve release tag (push) Blocked by required conditions
Docker Release / Build and publish all-in-one (push) Blocked by required conditions
Docker Release / Build and publish api (push) Blocked by required conditions
Docker Release / Build and publish web (push) Blocked by required conditions
Docker Release / Build and publish dovecot (push) Blocked by required conditions
Docker Release / Build and publish postfix (push) Blocked by required conditions
Docker Release / Build and publish rspamd (push) Blocked by required conditions
Docker Release / Create GitHub release (push) Blocked by required conditions

This commit is contained in:
zxyszx
2026-08-13 18:42:27 +08:00
parent 7fc91f2ed9
commit f15a3d3222
7 changed files with 60 additions and 53 deletions
+4
View File
@@ -0,0 +1,4 @@
- 将邮箱、个人设置和后台管理的当前选中项统一为清晰的淡蓝色,并补齐悬停、二级标签和邮件列表选中状态。
- 优化后台数据总览:合并重复指标,改为四项核心数据、紧凑首次配置和统一系统状态布局,并确保公网地址完整显示。
**完整更新日志**[v1.2.41...v1.2.42](https://github.com/zxyszx/NewSzxcn-Email/compare/v1.2.41...v1.2.42)
+1 -1
View File
@@ -1 +1 @@
1.2.41 1.2.42
+4 -4
View File
@@ -519,7 +519,7 @@ const SidebarMenuItem = React.forwardRef<
SidebarMenuItem.displayName = "SidebarMenuItem" SidebarMenuItem.displayName = "SidebarMenuItem"
const sidebarMenuButtonVariants = cva( const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-[hsl(var(--sidebar-active))] data-[active=true]:font-medium data-[active=true]:text-[hsl(var(--sidebar-active-foreground))] data-[active=true]:hover:bg-[hsl(var(--sidebar-active))] data-[active=true]:hover:text-[hsl(var(--sidebar-active-foreground))] data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{ {
variants: { variants: {
variant: { variant: {
@@ -621,7 +621,7 @@ const SidebarMenuAction = React.forwardRef<
"peer-data-[size=lg]/menu-button:top-2.5", "peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden", "group-data-[collapsible=icon]:hidden",
showOnHover && showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-[hsl(var(--sidebar-active-foreground))] md:opacity-0",
className className
)} )}
{...props} {...props}
@@ -639,7 +639,7 @@ const SidebarMenuBadge = React.forwardRef<
data-sidebar="menu-badge" data-sidebar="menu-badge"
className={cn( className={cn(
"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground", "pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground",
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-[hsl(var(--sidebar-active-foreground))]",
"peer-data-[size=sm]/menu-button:top-1", "peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5", "peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5", "peer-data-[size=lg]/menu-button:top-2.5",
@@ -730,7 +730,7 @@ const SidebarMenuSubButton = React.forwardRef<
data-active={isActive} data-active={isActive}
className={cn( className={cn(
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", "data-[active=true]:bg-[hsl(var(--sidebar-active))] data-[active=true]:text-[hsl(var(--sidebar-active-foreground))]",
size === "sm" && "text-xs", size === "sm" && "text-xs",
size === "md" && "text-sm", size === "md" && "text-sm",
"group-data-[collapsible=icon]:hidden", "group-data-[collapsible=icon]:hidden",
+11
View File
@@ -35,6 +35,9 @@
--sidebar-accent-foreground: 0 0% 12%; --sidebar-accent-foreground: 0 0% 12%;
--sidebar-border: 0 0% 89.8%; --sidebar-border: 0 0% 89.8%;
--sidebar-ring: 0 0% 42%; --sidebar-ring: 0 0% 42%;
--sidebar-active: 207 100% 92%;
--sidebar-active-foreground: 202 100% 30%;
--mail-selected: 210 100% 96%;
--compose-send: 217 89% 43%; --compose-send: 217 89% 43%;
--compose-send-hover: 216 94% 32%; --compose-send-hover: 216 94% 32%;
} }
@@ -86,6 +89,9 @@
--sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%; --sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%; --sidebar-ring: 217.2 91.2% 59.8%;
--sidebar-active: 210 48% 24%;
--sidebar-active-foreground: 210 100% 88%;
--mail-selected: 210 38% 20%;
--compose-send: 217 89% 52%; --compose-send: 217 89% 52%;
--compose-send-hover: 214 94% 60%; --compose-send-hover: 214 94% 60%;
} }
@@ -124,6 +130,11 @@
max-width: 384px; max-width: 384px;
} }
.mail-selected-row {
background-color: hsl(var(--mail-selected));
}
@media (max-width: 767px) { @media (max-width: 767px) {
.mail-content-grid { .mail-content-grid {
grid-template-columns: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
+28 -36
View File
@@ -2,7 +2,7 @@ import * as React from "react"
import DOMPurify from "dompurify" import DOMPurify from "dompurify"
import { useSearchParams } from "react-router-dom" import { useSearchParams } from "react-router-dom"
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { ArrowRight, BookOpen, CheckCircle2, ChevronDown, Circle, ClipboardList, Cloud, Copy, Download, ExternalLink, Eye, EyeOff, Github, Globe2, HardDrive, KeyRound, Loader2, Mail, Mailbox, MoreHorizontal, RefreshCcw, Scale, Search, Send, ShieldCheck, Star, Trash2, Users } from "lucide-react" import { BookOpen, CheckCircle2, ChevronDown, Circle, ClipboardList, Cloud, Copy, Download, ExternalLink, Eye, EyeOff, Github, Globe2, HardDrive, KeyRound, Loader2, Mail, Mailbox, MoreHorizontal, RefreshCcw, Scale, Search, Send, ShieldCheck, Star, Trash2, Users } from "lucide-react"
import { api, AdminUser, Alias, DNSRecord, Domain, Mailbox as MailboxType, MailMessage, MailTemplate, MaildirSyncHealth, PermissionGroup, PermissionInfo, PermissionLimits, SystemSettings } from "@/lib/api" import { api, AdminUser, Alias, DNSRecord, Domain, Mailbox as MailboxType, MailMessage, MailTemplate, MaildirSyncHealth, PermissionGroup, PermissionInfo, PermissionLimits, SystemSettings } from "@/lib/api"
import { cn, decodeMimeHeader, formatBytes, formatDate } from "@/lib/utils" import { cn, decodeMimeHeader, formatBytes, formatDate } from "@/lib/utils"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@@ -135,18 +135,18 @@ export function AdminPage() {
return ( return (
<ScrollArea className="h-[calc(100svh-3rem)] md:h-svh"> <ScrollArea className="h-[calc(100svh-3rem)] md:h-svh">
<main className="admin-page mx-auto w-full max-w-[1180px] px-3 pb-10 pt-3 sm:px-4 sm:pt-4"> <main className="admin-page mx-auto w-full max-w-[1440px] px-3 pb-8 pt-3 sm:px-4 sm:pt-4">
<AdminPageHeader section={section} refreshing={refreshing} onRefresh={refreshAdminPage} /> <AdminPageHeader section={section} refreshing={refreshing} onRefresh={refreshAdminPage} />
{sectionQuery?.isError && <QueryFailure error={sectionQuery.error} onRetry={() => { void sectionQuery.refetch() }} />} {sectionQuery?.isError && <QueryFailure error={sectionQuery.error} onRetry={() => { void sectionQuery.refetch() }} />}
{section === "overview" && canOverview && ( {section === "overview" && canOverview && (
<div className="mb-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4"> <section className="mb-3 grid grid-cols-2 gap-3 lg:grid-cols-4">
<Stat icon={<Users />} label="账号" value={overview.data?.users || 0} /> <Stat icon={<Users />} label="账号" value={overview.data?.users || 0} detail={`${overview.data?.activeUsers || 0} 个活跃`} />
<Stat icon={<Globe2 />} label="邮件域名" value={overview.data?.domains || 0} /> <Stat icon={<Globe2 />} label="邮件域名" value={overview.data?.domains || 0} detail={`${domainItems.filter((domain) => domain.dnsStatus === "ok").length} 个 DNS 正常`} />
<Stat icon={<Mailbox />} label="邮箱" value={overview.data?.mailboxes || 0} /> <Stat icon={<Mailbox />} label="邮箱" value={overview.data?.mailboxes || 0} detail={`${overview.data?.activeMailboxes || 0} 个活跃`} />
<Stat icon={<ShieldCheck />} label="存储用量" value={formatBytes(overview.data?.storageBytes || 0)} /> <Stat icon={<ShieldCheck />} label="存储用量" value={formatBytes(overview.data?.storageBytes || 0)} detail={`${overview.data?.unreadMessages || 0} 封未读 · ${overview.data?.aliases || 0} 个转发`} />
</div> </section>
)} )}
{section === "overview" && <OverviewSection overview={overview.data} domains={domainItems} settings={settings.data} visibleSections={visibleSections} onSectionChange={(next) => setParams(next === "overview" ? {} : { section: next })} />} {section === "overview" && <OverviewSection overview={overview.data} domains={domainItems} settings={settings.data} visibleSections={visibleSections} onSectionChange={(next) => setParams(next === "overview" ? {} : { section: next })} />}
@@ -191,52 +191,44 @@ function AdminPageHeader({ section, refreshing, onRefresh }: { section: Section;
function OverviewSection({ overview, domains, settings, visibleSections, onSectionChange }: { overview?: { activeUsers: number; activeMailboxes: number; aliases: number; messages: number; unreadMessages: number }; domains: Domain[]; settings?: SystemSettings; visibleSections: Section[]; onSectionChange: (section: Section) => void }) { function OverviewSection({ overview, domains, settings, visibleSections, onSectionChange }: { overview?: { activeUsers: number; activeMailboxes: number; aliases: number; messages: number; unreadMessages: number }; domains: Domain[]; settings?: SystemSettings; visibleSections: Section[]; onSectionChange: (section: Section) => void }) {
const checklist = setupChecklist(overview, domains, settings).filter((item) => visibleSections.includes(item.section)) const checklist = setupChecklist(overview, domains, settings).filter((item) => visibleSections.includes(item.section))
return ( return (
<div className="space-y-6"> <div className="grid items-start gap-3 lg:grid-cols-[minmax(0,1.35fr)_minmax(340px,.65fr)]">
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_420px]">
<Card> <Card>
<CardHeader><CardTitle></CardTitle></CardHeader> <CardHeader className="pb-3"><CardTitle></CardTitle></CardHeader>
<CardContent className="grid gap-3 md:grid-cols-2 xl:grid-cols-4"> <CardContent className="grid gap-2 sm:grid-cols-2">
<InfoBox label="活跃账号" value={overview?.activeUsers || 0} />
<InfoBox label="活跃邮箱" value={overview?.activeMailboxes || 0} />
<InfoBox label="邮件转发" value={overview?.aliases || 0} />
<InfoBox label="未读邮件" value={overview?.unreadMessages || 0} />
</CardContent>
</Card>
<Card>
<CardHeader><CardTitle></CardTitle></CardHeader>
<CardContent className="space-y-2">
{checklist.map((item) => ( {checklist.map((item) => (
<Button key={item.key} type="button" variant="outline" className="h-auto w-full justify-start gap-3 px-3 py-2 text-left font-normal" onClick={() => onSectionChange(item.section)}> <Button key={item.key} type="button" variant="outline" className="h-auto min-h-[64px] w-full justify-start gap-3 px-3 py-2 text-left font-normal last:sm:col-span-2" onClick={() => onSectionChange(item.section)}>
{item.done ? <CheckCircle2 className="h-4 w-4 shrink-0 text-green-600" /> : <Circle className="h-4 w-4 shrink-0 text-muted-foreground" />} {item.done ? <CheckCircle2 className="h-4 w-4 shrink-0 text-green-600" /> : <Circle className="h-4 w-4 shrink-0 text-muted-foreground" />}
<span className="min-w-0 flex-1"> <span className="min-w-0 flex-1">
<span className="block font-medium">{item.title}</span> <span className="block font-medium">{item.title}</span>
<span className="block truncate text-xs text-muted-foreground">{item.detail}</span> <span className="block truncate text-xs text-muted-foreground">{item.detail}</span>
</span> </span>
<ArrowRight className="h-4 w-4 shrink-0 text-muted-foreground" />
</Button> </Button>
))} ))}
</CardContent> </CardContent>
</Card> </Card>
</div>
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_420px]">
<Card> <Card>
<CardHeader><CardTitle>DNS </CardTitle></CardHeader> <CardHeader className="pb-3"><CardTitle></CardTitle></CardHeader>
<CardContent className="space-y-2"> <CardContent className="space-y-4">
<section>
<h3 className="mb-2 text-xs font-medium text-muted-foreground">DNS </h3>
<div className="space-y-2">
{domains.map((domain) => <DomainBadgeRow key={domain.id} domain={domain} />)} {domains.map((domain) => <DomainBadgeRow key={domain.id} domain={domain} />)}
{domains.length === 0 && <Empty text="暂无域名" />} {domains.length === 0 && <Empty text="暂无域名" />}
</CardContent> </div>
</Card> </section>
<Card> <Separator />
<CardHeader><CardTitle></CardTitle></CardHeader> <section>
<CardContent className="space-y-3 text-sm text-muted-foreground"> <h3 className="mb-2 text-xs font-medium text-muted-foreground"></h3>
<div className="space-y-2 text-sm text-muted-foreground">
<InfoLine label="公网地址" value={settings?.publicBaseUrl || "-"} /> <InfoLine label="公网地址" value={settings?.publicBaseUrl || "-"} />
<InfoLine label="SMTP" value={settings?.smtpHost ? `${settings.smtpHost}:${settings.smtpPort}` : "-"} /> <InfoLine label="SMTP" value={settings?.smtpHost ? `${settings.smtpHost}:${settings.smtpPort}` : "-"} />
<InfoLine label="注册" value={settings?.openRegistration ? "已开放" : "关闭"} /> <InfoLine label="注册" value={settings?.openRegistration ? "已开放" : "关闭"} />
<InfoLine label="自助申请邮箱" value={settings?.userMailboxApplyEnabled ? "已启用" : "关闭"} /> <InfoLine label="自助申请邮箱" value={settings?.userMailboxApplyEnabled ? "已启用" : "关闭"} />
</div>
</section>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</div>
) )
} }
@@ -255,7 +247,7 @@ function setupChecklist(overview: { activeUsers: number; activeMailboxes: number
} }
function InfoLine({ label, value }: { label: string; value: React.ReactNode }) { function InfoLine({ label, value }: { label: string; value: React.ReactNode }) {
return <div className="flex items-center justify-between gap-3 rounded-md border px-3 py-2"><span>{label}</span><span className="min-w-0 truncate font-medium text-foreground">{value}</span></div> return <div className="grid min-h-10 grid-cols-[auto_minmax(0,1fr)] items-center gap-3 rounded-md border px-3 py-2"><span className="whitespace-nowrap">{label}</span><span className="min-w-0 break-all text-right font-medium text-foreground">{value}</span></div>
} }
function generateBackupPassword(length = 24) { function generateBackupPassword(length = 24) {
@@ -2223,8 +2215,8 @@ function sendAuditBadgeVariant(event?: string) {
return "secondary" return "secondary"
} }
function Stat({ icon, label, value }: { icon: React.ReactNode; label: string; value: React.ReactNode }) { function Stat({ icon, label, value, detail }: { icon: React.ReactNode; label: string; value: React.ReactNode; detail: string }) {
return <Card><CardContent className="flex items-center gap-3 p-4 sm:gap-4 sm:p-5"><div className="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-muted text-foreground sm:h-10 sm:w-10">{icon}</div><div className="min-w-0"><div className="truncate text-xl font-semibold tracking-tight sm:text-2xl">{value}</div><div className="text-xs text-muted-foreground">{label}</div></div></CardContent></Card> return <Card><CardContent className="flex min-h-[84px] items-center gap-3 p-4"><div className="grid h-9 w-9 shrink-0 place-items-center rounded-md bg-muted text-foreground [&>svg]:h-4 [&>svg]:w-4">{icon}</div><div className="min-w-0"><div className="flex items-baseline gap-2"><div className="truncate text-xl font-semibold leading-7">{value}</div><div className="truncate text-sm text-muted-foreground">{label}</div></div><div className="mt-0.5 truncate text-xs text-muted-foreground">{detail}</div></div></CardContent></Card>
} }
function InfoBox({ label, value }: { label: string; value: React.ReactNode }) { return <div className="rounded-lg border p-4"><div className="text-xl font-semibold tracking-tight sm:text-2xl">{value}</div><div className="text-xs text-muted-foreground">{label}</div></div> } function InfoBox({ label, value }: { label: string; value: React.ReactNode }) { return <div className="rounded-lg border p-4"><div className="text-xl font-semibold tracking-tight sm:text-2xl">{value}</div><div className="text-xs text-muted-foreground">{label}</div></div> }
function Empty({ text }: { text: string }) { return <div className="rounded-lg border border-dashed p-8 text-center text-sm text-muted-foreground">{text}</div> } function Empty({ text }: { text: string }) { return <div className="rounded-lg border border-dashed p-8 text-center text-sm text-muted-foreground">{text}</div> }
+3 -3
View File
@@ -3393,7 +3393,7 @@ function CompactMessageRow({ message, active, checked, scheduled, onCheckedChang
<div onClick={onClick} onContextMenu={onContextMenu} className={cn( <div onClick={onClick} onContextMenu={onContextMenu} className={cn(
"cursor-pointer border-b border-l-2 px-3 py-2.5 text-[13px] transition-colors sm:grid sm:grid-cols-[28px_24px_minmax(112px,180px)_minmax(0,1fr)_86px_30px] sm:items-center sm:gap-2 sm:px-3 sm:py-2", "cursor-pointer border-b border-l-2 px-3 py-2.5 text-[13px] transition-colors sm:grid sm:grid-cols-[28px_24px_minmax(112px,180px)_minmax(0,1fr)_86px_30px] sm:items-center sm:gap-2 sm:px-3 sm:py-2",
message.isRead ? "border-l-transparent hover:bg-accent/50" : "border-l-primary bg-primary/5 font-semibold hover:bg-primary/10", message.isRead ? "border-l-transparent hover:bg-accent/50" : "border-l-primary bg-primary/5 font-semibold hover:bg-primary/10",
active && "bg-accent" active && "mail-selected-row"
)}> )}>
<div className="flex gap-3 sm:contents"> <div className="flex gap-3 sm:contents">
<Checkbox aria-label="选择邮件" checked={checked} onCheckedChange={(value) => onCheckedChange(value === true)} onClick={(event) => event.stopPropagation()} className="mt-0.5 shrink-0 sm:mt-0" /> <Checkbox aria-label="选择邮件" checked={checked} onCheckedChange={(value) => onCheckedChange(value === true)} onClick={(event) => event.stopPropagation()} className="mt-0.5 shrink-0 sm:mt-0" />
@@ -3830,8 +3830,8 @@ function MessageRow({
return <div onClick={onClick} onContextMenu={onContextMenu} className={cn( return <div onClick={onClick} onContextMenu={onContextMenu} className={cn(
"group cursor-pointer border-b border-l-2 px-3 py-2.5 transition-colors", "group cursor-pointer border-b border-l-2 px-3 py-2.5 transition-colors",
message.isRead ? "border-l-transparent hover:bg-accent/60" : "border-l-primary bg-primary/5 font-semibold hover:bg-primary/10", message.isRead ? "border-l-transparent hover:bg-accent/60" : "border-l-primary bg-primary/5 font-semibold hover:bg-primary/10",
active && "bg-accent", active && "mail-selected-row",
checked && "bg-accent/70" checked && "mail-selected-row"
)}> )}>
<div className="flex gap-2.5"> <div className="flex gap-2.5">
<Checkbox <Checkbox
+3 -3
View File
@@ -356,11 +356,11 @@ export function ProfilePage() {
type="button" type="button"
className={cn( className={cn(
"flex h-9 w-full items-center gap-2 rounded-md px-3 text-left text-sm transition-colors", "flex h-9 w-full items-center gap-2 rounded-md px-3 text-left text-sm transition-colors",
tab === key ? "bg-muted font-semibold text-foreground" : "text-muted-foreground hover:bg-muted/70 hover:text-foreground", tab === key ? "bg-[hsl(var(--sidebar-active))] font-semibold text-[hsl(var(--sidebar-active-foreground))]" : "text-muted-foreground hover:bg-muted/70 hover:text-foreground",
)} )}
onClick={() => setTab(key)} onClick={() => setTab(key)}
> >
<span className={cn("text-muted-foreground [&>svg]:h-4 [&>svg]:w-4 [&>svg]:stroke-[1.8]", tab === key && "text-foreground/70")}>{tabs[key].icon}</span> <span className={cn("text-muted-foreground [&>svg]:h-4 [&>svg]:w-4 [&>svg]:stroke-[1.8]", tab === key && "text-[hsl(var(--sidebar-active-foreground))]")}>{tabs[key].icon}</span>
<span className="truncate">{tabs[key].label}</span> <span className="truncate">{tabs[key].label}</span>
</button> </button>
))} ))}
@@ -563,7 +563,7 @@ function SettingsPageHeader({ title, subtitle, action, activeTab, onAccountTabCh
type="button" type="button"
className={cn( className={cn(
"h-[38px] shrink-0 border-b-2 px-4 text-sm font-medium transition-colors", "h-[38px] shrink-0 border-b-2 px-4 text-sm font-medium transition-colors",
activeTab === item.key ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground", activeTab === item.key ? "border-[hsl(var(--sidebar-active-foreground))] bg-[hsl(var(--sidebar-active))] text-[hsl(var(--sidebar-active-foreground))]" : "border-transparent text-muted-foreground hover:text-foreground",
)} )}
onClick={() => onAccountTabChange(item.key)} onClick={() => onAccountTabChange(item.key)}
> >