quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 51s
quality / api (push) Successful in 43s
CD / quality (push) Successful in 1m43s
CD / publish (push) Successful in 1m38s
- Added functionality to toggle individual IPs for services, allowing for dynamic management of IP health status. - Enhanced the health check configuration in the UI, enabling users to set parameters directly from the settings page. - Updated service views to include IP health tracking, improving visibility into the status of each IP associated with a service. - Refactored relevant components to support the new IP toggling feature, ensuring a seamless user experience. This commit significantly enhances the health management capabilities of services, providing users with more control over IP configurations and health monitoring.
105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import { Link, Outlet, useRouterState } from '@tanstack/react-router'
|
|
import { HeartPulseIcon, PaletteIcon, SettingsIcon } from 'lucide-react'
|
|
|
|
import { useIsMobile } from '@cfdm/ui/hooks/use-mobile'
|
|
import { cn } from '@cfdm/ui/lib/utils'
|
|
import { PageShell } from '@/components/page-shell'
|
|
import { PageHeader } from '@/components/page-header'
|
|
|
|
export interface SettingsTabConfig {
|
|
id: string
|
|
to: string
|
|
label: string
|
|
icon?: ReactNode
|
|
}
|
|
|
|
const DEFAULT_TABS: SettingsTabConfig[] = [
|
|
{
|
|
id: 'appearance',
|
|
to: '/settings/appearance',
|
|
label: 'Внешний вид',
|
|
icon: <PaletteIcon className="size-4" aria-hidden="true" />,
|
|
},
|
|
{
|
|
id: 'health',
|
|
to: '/settings/health',
|
|
label: 'Health-check',
|
|
icon: <HeartPulseIcon className="size-4" aria-hidden="true" />,
|
|
},
|
|
{
|
|
id: 'integrations',
|
|
to: '/settings/integrations',
|
|
label: 'Интеграции',
|
|
icon: <SettingsIcon className="size-4" aria-hidden="true" />,
|
|
},
|
|
]
|
|
|
|
interface SettingsShellProps {
|
|
title?: string
|
|
description?: string
|
|
tabs?: SettingsTabConfig[]
|
|
}
|
|
|
|
export function SettingsShell({
|
|
title = 'Настройки',
|
|
description = 'Внешний вид, health-check и интеграции',
|
|
tabs = DEFAULT_TABS,
|
|
}: SettingsShellProps) {
|
|
const isMobile = useIsMobile()
|
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
|
|
return (
|
|
<PageShell>
|
|
<div className="mx-auto flex w-full max-w-4xl flex-col gap-5">
|
|
<PageHeader title={title} description={description} />
|
|
|
|
<div
|
|
className={cn(
|
|
'flex gap-5',
|
|
isMobile ? 'flex-col' : 'flex-row items-start',
|
|
)}
|
|
>
|
|
{tabs.length > 1 ? (
|
|
<nav
|
|
aria-label="Разделы настроек"
|
|
className={cn(
|
|
'flex gap-1',
|
|
isMobile
|
|
? 'scrollbar-none -mx-1 overflow-x-auto overflow-y-hidden pb-1'
|
|
: 'w-44 shrink-0 flex-col',
|
|
)}
|
|
>
|
|
{tabs.map((tab) => {
|
|
const isActive = pathname.startsWith(tab.to)
|
|
return (
|
|
<Link
|
|
key={tab.id}
|
|
to={tab.to}
|
|
aria-current={isActive ? 'page' : undefined}
|
|
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 shadow-sm ring-1 ring-border/60'
|
|
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground',
|
|
)}
|
|
>
|
|
{tab.icon}
|
|
{tab.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
</nav>
|
|
) : null}
|
|
|
|
<div className="min-w-0 flex-1">
|
|
<Outlet />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</PageShell>
|
|
)
|
|
}
|