-
- {title}
-
+
+
+
{title}
{description ? (
-
- {description}
-
+
{description}
) : null}
- {actions ? (
-
- {actions}
-
- ) : null}
-
+ {actions ?
{actions}
: null}
+
)
}
diff --git a/apps/web/src/routes/_auth/settings/appearance.tsx b/apps/web/src/routes/_auth/settings/appearance.tsx
index 68ef36a..7343837 100644
--- a/apps/web/src/routes/_auth/settings/appearance.tsx
+++ b/apps/web/src/routes/_auth/settings/appearance.tsx
@@ -1,11 +1,13 @@
-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
-import { PaletteIcon } from 'lucide-react'
+import { LogOutIcon, PaletteIcon } from 'lucide-react'
import { api } from '@/lib/api-client'
+import { clearToken } from '@/lib/auth'
import { SettingRow } from '@/components/setting-row'
import { Switch } from '@cfdm/ui/components/switch'
+import { Button } from '@cfdm/ui/components/button'
import {
Frame,
FrameDescription,
@@ -29,6 +31,7 @@ export const Route = createFileRoute('/_auth/settings/appearance')({
})
function AppearanceSettingsPage() {
+ const navigate = useNavigate()
const queryClient = useQueryClient()
const { data, isLoading } = useQuery({
queryKey: ['app-settings'],
@@ -48,6 +51,11 @@ function AppearanceSettingsPage() {
const showQuickActions = data?.showQuickActions !== false
+ const handleLogout = () => {
+ clearToken()
+ void navigate({ to: '/login' })
+ }
+
return (
@@ -62,7 +70,6 @@ function AppearanceSettingsPage() {
+
+
+
diff --git a/docs/ui-design-contract.md b/docs/ui-design-contract.md
index 02035d8..ac9f98b 100644
--- a/docs/ui-design-contract.md
+++ b/docs/ui-design-contract.md
@@ -49,18 +49,22 @@ Gating: DB `show_quick_actions` / `showQuickActions` / `ui_show_quick_actions` (
## Shared App Shell chrome
-Эталон: CFDM + ReUI [app-shell-12](https://reui.io/preview/base/app-shell-12) · monitor/switchers [app-shell-7](https://reui.io/preview/base/app-shell-7).
+Эталон: **EvoBGP** production AppShell + ReUI [app-shell-12](https://reui.io/preview/base/app-shell-12).
-При переключении между vps-tracker / CFDM / EvoBGP меняются **только** sidebar nav и `main` content. ClassNames chrome идентичны.
+При переключении между vps-tracker / CFDM / EvoBGP меняются **только** sidebar nav labels/hrefs и `main` content. Разметка, ширина, фон и hover chrome идентичны.
| Токен / зона | Значение |
|--------------|----------|
-| `--sidebar-width` | `240px` |
-| Sidebar accent | primary **14%** mix + transparent border |
-| Header | `h-12`, `sticky`, `border-b`, `px-4 md:px-6` (без blur / без `h-16`) |
-| Header right | **AppsMenu** → **SystemMonitorPopover** → **ModeToggle** |
+| `SIDEBAR_WIDTH` / `--sidebar-width` | `240px` (в `packages/ui` sidebar + Provider style) |
+| Sidebar / hover colors | theme `--sidebar` / `--sidebar-accent` из `globals.css` — **без** AppShell `color-mix` override |
+| Header | `h-12`, `sticky`, `border-b`, `px-4 md:px-6` |
+| Header left | `SidebarTrigger` + `Separator` + Breadcrumb |
+| Header right | **AppsMenu** → **SystemMonitorPopover** → **ModeToggle** (без Search в chrome) |
+| Sidebar | AppSwitcher → groups (`SidebarGroupContent`) → icons `size-4` → **пустой** `SidebarFooter` |
| `main` | `gap-4 md:gap-6`, `px-4 py-4 md:px-6 md:py-5` |
-| Sidebar header | `AppSwitcher` (не static brand) |
+| Search | hotkey ⌘K / Ctrl+K only (не кнопка в header) |
+
+Запрещено в chrome: `SidebarRail`, `NavUser` footer, sync-row footer, Search/Ctrl+K pill в header, issues Badge в header, muted/hover cascade на right-cluster, Provider `color-mix` для `--sidebar*`.
App Switcher ids: `vps-tracker` · `cfdm` · `evobgp`. Override: `VITE_APP_SWITCHER` JSON.
diff --git a/packages/ui/src/components/sidebar.tsx b/packages/ui/src/components/sidebar.tsx
index fc7735e..b18899d 100644
--- a/packages/ui/src/components/sidebar.tsx
+++ b/packages/ui/src/components/sidebar.tsx
@@ -25,7 +25,7 @@ import { PanelLeftIcon } from "lucide-react"
const SIDEBAR_COOKIE_NAME = "sidebar_state"
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
-const SIDEBAR_WIDTH = "16rem"
+const SIDEBAR_WIDTH = "240px"
const SIDEBAR_WIDTH_MOBILE = "18rem"
const SIDEBAR_WIDTH_ICON = "3rem"
const SIDEBAR_KEYBOARD_SHORTCUT = "b"