feat(web): enhance integration settings and UI components
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m25s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m49s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 5s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped

- Updated AppSwitcherEditor to include item separators for better visual organization.
- Improved VpsTrackerIntegrationCard with badges indicating synchronization status and token configuration.
- Refactored SettingsShell to streamline layout and enhance accessibility with improved navigation.
- Added detail fields to KPI cards in DashboardPage for clearer information presentation.
- Introduced new components in the ReUI kit for better integration and user experience.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-10 01:10:34 +07:00
co-authored by Cursor
parent 1de497d4b4
commit 781c88175c
9 changed files with 462 additions and 325 deletions
@@ -4,12 +4,6 @@ import { SettingsIcon } from 'lucide-react'
import { useIsMobile } from '@cfdm/ui/hooks/use-mobile'
import { cn } from '@cfdm/ui/lib/utils'
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@cfdm/ui/components/tabs'
import { PageShell } from '@/components/page-shell'
export interface SettingsTabConfig {
@@ -32,23 +26,19 @@ interface SettingsShellProps {
title?: string
description?: string
tabs?: SettingsTabConfig[]
children?: ReactNode
}
export function SettingsShell({
title = 'Настройки',
description = 'Интеграции и конфигурация приложения',
tabs = DEFAULT_TABS,
children,
}: SettingsShellProps) {
const isMobile = useIsMobile()
const pathname = useRouterState({ select: (s) => s.location.pathname })
const activeTab =
tabs.find((tab) => pathname.startsWith(tab.to))?.id ?? tabs[0]?.id ?? ''
return (
<PageShell>
<div className="flex w-full max-w-4xl flex-col gap-6">
<div className="mx-auto flex w-full max-w-4xl flex-col gap-6">
<header className="px-1">
<h1 className="text-xl font-semibold tracking-tight">{title}</h1>
<p className="text-muted-foreground max-w-2xl text-sm leading-relaxed">
@@ -56,45 +46,49 @@ export function SettingsShell({
</p>
</header>
<Tabs value={activeTab} orientation={isMobile ? 'horizontal' : 'vertical'}>
<div
className={cn(
'flex gap-6',
isMobile ? 'flex-col' : 'flex-row items-start',
)}
>
<TabsList
variant="line"
<div
className={cn(
'flex gap-6',
isMobile ? 'flex-col' : 'flex-row items-start',
)}
>
{tabs.length > 1 ? (
<nav
aria-label="Разделы настроек"
className={cn(
'h-auto w-full justify-start gap-1 bg-transparent p-0',
!isMobile && 'w-48 shrink-0 flex-col items-stretch',
'flex gap-1',
isMobile
? 'scrollbar-none -mx-1 overflow-x-auto pb-1'
: 'w-44 shrink-0 flex-col',
)}
>
{tabs.map((tab) => (
<TabsTrigger
key={tab.id}
value={tab.id}
className={cn(
'justify-start gap-2 px-3 py-2',
!isMobile && 'w-full',
)}
render={<Link to={tab.to} />}
>
{tab.icon}
{tab.label}
</TabsTrigger>
))}
</TabsList>
{tabs.map((tab) => {
const isActive = pathname.startsWith(tab.to)
return (
<Link
key={tab.id}
to={tab.to}
className={cn(
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
isMobile && 'shrink-0',
!isMobile && 'w-full',
isActive
? 'bg-muted text-foreground font-medium'
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground',
)}
>
{tab.icon}
{tab.label}
</Link>
)
})}
</nav>
) : null}
<div className="min-w-0 flex-1">
{tabs.map((tab) => (
<TabsContent key={tab.id} value={tab.id} className="mt-0">
{children ?? <Outlet />}
</TabsContent>
))}
</div>
<div className="min-w-0 flex-1">
<Outlet />
</div>
</Tabs>
</div>
</div>
</PageShell>
)