From 74af90270fc46b6da236cf906847f59fa86a4228 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 19 Jul 2026 04:54:42 +0700 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=82=D1=8C=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D0=BD=D0=B0=20ReUI=20Fr?= =?UTF-8?q?ame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Разбить монолитную Card-форму на вкладки Общие/Синхронизация/Уведомления/Интеграции с SettingRow и SettingsCard по эталонам settings-16/3/2. Co-authored-by: Cursor --- .../integrations/cfdm-integration-card.tsx | 185 ++-- apps/web/src/components/reui-kit/index.ts | 1 + .../src/components/reui-kit/settings-card.tsx | 55 ++ .../components/reui-kit/settings-shell.tsx | 19 +- apps/web/src/components/setting-row.tsx | 85 ++ .../settings/use-settings-section.ts | 97 +++ apps/web/src/routeTree.gen.ts | 43 + apps/web/src/routes/_auth/settings/index.tsx | 806 ++++++------------ .../routes/_auth/settings/integrations.tsx | 257 +++++- .../routes/_auth/settings/notifications.tsx | 429 ++++++++++ apps/web/src/routes/_auth/settings/sync.tsx | 136 +++ packages/ui/src/components/collapsible.tsx | 19 + 12 files changed, 1458 insertions(+), 674 deletions(-) create mode 100644 apps/web/src/components/reui-kit/settings-card.tsx create mode 100644 apps/web/src/components/setting-row.tsx create mode 100644 apps/web/src/components/settings/use-settings-section.ts create mode 100644 apps/web/src/routes/_auth/settings/notifications.tsx create mode 100644 apps/web/src/routes/_auth/settings/sync.tsx create mode 100644 packages/ui/src/components/collapsible.tsx diff --git a/apps/web/src/components/integrations/cfdm-integration-card.tsx b/apps/web/src/components/integrations/cfdm-integration-card.tsx index 7014f86..c051219 100644 --- a/apps/web/src/components/integrations/cfdm-integration-card.tsx +++ b/apps/web/src/components/integrations/cfdm-integration-card.tsx @@ -3,13 +3,12 @@ import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { toast } from 'sonner' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@cfdm/ui/components/card' +import { SettingRow } from '@/components/setting-row' +import { LoadingButton } from '@/components/loading-button' import { FieldGroup } from '@cfdm/ui/components/field' import { Input } from '@cfdm/ui/components/input' -import { FormField } from '@/components/form-field' -import { SelectField } from '@/components/select-field' -import { LoadingButton } from '@/components/loading-button' import { Button } from '@cfdm/ui/components/button' +import { Switch } from '@cfdm/ui/components/switch' import type { Settings } from '@/types/entities' const formSchema = z.object({ @@ -26,7 +25,7 @@ function generateToken(): string { return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('') } -interface CfdmIntegrationCardProps { +interface CfdmIntegrationFormProps { settings?: Settings onSave: (values: { cfdmApiUrl?: string @@ -36,7 +35,12 @@ interface CfdmIntegrationCardProps { isSaving?: boolean } -export function CfdmIntegrationCard({ settings, onSave, isSaving }: CfdmIntegrationCardProps) { +/** CFDM integration form — Frame/SettingRow, no Card. Preview https://reui.io/preview/base/settings-3 */ +export function CfdmIntegrationForm({ + settings, + onSave, + isSaving, +}: CfdmIntegrationFormProps) { const form = useForm({ resolver: zodResolver(formSchema), values: { @@ -56,79 +60,104 @@ export function CfdmIntegrationCard({ settings, onSave, isSaving }: CfdmIntegrat } return ( - - - CF Domain Manager - - Приём синхронизации доменов и сервисов из CFDM. Скопируйте токен в настройки CFDM. - - - -
void form.handleSubmit(handleSubmit)(e)}> - - - void form.handleSubmit(handleSubmit)(e)} + > + + + ( + - - -
- - -
-
- ( - - field.onChange((v ?? 'on') === 'on')} - options={[ - { value: 'on', label: 'Вкл' }, - { value: 'off', label: 'Выкл' }, - ]} - /> - - )} + )} + /> + + + + + +
+ - {settings?.integrationLastSyncAt ? ( -

- Последний sync: {new Date(settings.integrationLastSyncAt).toLocaleString('ru-RU')} -

- ) : null} - - - Сохранить интеграцию - - - - + +
+
+ {settings?.integrationLastSyncAt ? ( + + + {new Date(settings.integrationLastSyncAt).toLocaleString('ru-RU')} + + + ) : null} +
+
+ + Сохранить интеграцию + +
+ ) } + +/** @deprecated Use CfdmIntegrationForm */ +export const CfdmIntegrationCard = CfdmIntegrationForm diff --git a/apps/web/src/components/reui-kit/index.ts b/apps/web/src/components/reui-kit/index.ts index 8614611..b4f875f 100644 --- a/apps/web/src/components/reui-kit/index.ts +++ b/apps/web/src/components/reui-kit/index.ts @@ -13,4 +13,5 @@ export { export { OpsDashboard } from './ops-dashboard' export { DetailPanel, type DetailMetricCard } from './detail-panel' export { SettingsShell, type SettingsTabConfig } from './settings-shell' +export { SettingsCard } from './settings-card' export { TopologyCanvas } from './topology-canvas' diff --git a/apps/web/src/components/reui-kit/settings-card.tsx b/apps/web/src/components/reui-kit/settings-card.tsx new file mode 100644 index 0000000..e58b67b --- /dev/null +++ b/apps/web/src/components/reui-kit/settings-card.tsx @@ -0,0 +1,55 @@ +import { type ReactNode } from 'react' + +import { cn } from '@cfdm/ui/lib/utils' +import { + Frame, + FrameDescription, + FrameFooter, + FrameHeader, + FramePanel, + FrameTitle, +} from '@/components/reui/frame' + +interface SettingsCardProps { + title: string + description?: string + children: ReactNode + footer?: ReactNode + className?: string + contentClassName?: string + footerClassName?: string + headerClassName?: string +} + +/** Settings section Frame — preview https://reui.io/preview/base/settings-16 · https://reui.io/preview/base/settings-3 */ +export function SettingsCard({ + title, + description, + children, + footer, + className, + contentClassName, + footerClassName, + headerClassName, +}: SettingsCardProps) { + return ( + + + + {title} + {description ? {description} : null} + + +
{children}
+ + {footer ? ( + + {footer} + + ) : null} +
+ + ) +} diff --git a/apps/web/src/components/reui-kit/settings-shell.tsx b/apps/web/src/components/reui-kit/settings-shell.tsx index 1f162b9..ca95b67 100644 --- a/apps/web/src/components/reui-kit/settings-shell.tsx +++ b/apps/web/src/components/reui-kit/settings-shell.tsx @@ -1,6 +1,11 @@ import type { ReactNode } from 'react' import { Link, Outlet, useRouterState } from '@tanstack/react-router' -import { PlugIcon, SettingsIcon } from 'lucide-react' +import { + BellIcon, + PlugIcon, + RefreshCwIcon, + SettingsIcon, +} from 'lucide-react' import { useIsMobile } from '@cfdm/ui/hooks/use-mobile' import { cn } from '@cfdm/ui/lib/utils' @@ -24,6 +29,18 @@ const DEFAULT_TABS: SettingsTabConfig[] = [ exact: true, icon: