diff --git a/apps/web/src/design-preview/bits.tsx b/apps/web/src/components/portal/bits.tsx similarity index 99% rename from apps/web/src/design-preview/bits.tsx rename to apps/web/src/components/portal/bits.tsx index daa4fbf..0aafd38 100644 --- a/apps/web/src/design-preview/bits.tsx +++ b/apps/web/src/components/portal/bits.tsx @@ -33,7 +33,7 @@ import { SheetHeader, SheetTitle, } from '@authportal/ui/components/sheet' -import type { Tone } from './mock' +import type { Tone } from './tone' import { TONE_BADGE } from './tone' const TONE_GLYPH: Record = { ok: '●', wn: '▲', er: '✕', mu: '○', in: '' } @@ -104,7 +104,7 @@ export function SignalAlert({ } /** Боковое окно (как CreateUserSheet): заголовок, прокручиваемое тело, подвал. */ -export function PreviewSheet({ +export function PortalSheet({ open, onOpenChange, title, diff --git a/apps/web/src/design-preview/canvas-blocks.tsx b/apps/web/src/components/portal/canvas-blocks.tsx similarity index 99% rename from apps/web/src/design-preview/canvas-blocks.tsx rename to apps/web/src/components/portal/canvas-blocks.tsx index 83c13e8..9933f1c 100644 --- a/apps/web/src/design-preview/canvas-blocks.tsx +++ b/apps/web/src/components/portal/canvas-blocks.tsx @@ -3,7 +3,7 @@ import { Badge } from '@/components/reui/badge' import { Frame, FrameDescription, FrameHeader, FramePanel, FrameTitle } from '@/components/reui/frame' import { IconTile } from '@/components/reui/icon-tile' import { cn } from '@authportal/ui/lib/utils' -import type { Tone } from './mock' +import type { Tone } from './tone' import { StatusBadge } from './bits' export type AppCardItem = { diff --git a/apps/web/src/design-preview/explain-banner.tsx b/apps/web/src/components/portal/explain-banner.tsx similarity index 96% rename from apps/web/src/design-preview/explain-banner.tsx rename to apps/web/src/components/portal/explain-banner.tsx index a6ddc68..01307d5 100644 --- a/apps/web/src/design-preview/explain-banner.tsx +++ b/apps/web/src/components/portal/explain-banner.tsx @@ -2,7 +2,7 @@ import { useState, type ReactNode } from 'react' import { useNavigate } from '@tanstack/react-router' import { Button } from '@authportal/ui/components/button' import { cn } from '@authportal/ui/lib/utils' -import { CodeBlock, PreviewSheet, SignalAlert, StatusBadge } from './bits' +import { CodeBlock, PortalSheet, SignalAlert, StatusBadge } from './bits' /** * Баннер доски: заголовок и пояснение, справа «Объяснить» — открывает панель @@ -38,7 +38,7 @@ export function ExplainBanner({ Объяснить - Блокировка и сброс passkeys выполняются вручную с подтверждением и попадают в журнал «Изменения». - + ) } diff --git a/apps/web/src/design-preview/kpi-strip.tsx b/apps/web/src/components/portal/kpi-strip.tsx similarity index 100% rename from apps/web/src/design-preview/kpi-strip.tsx rename to apps/web/src/components/portal/kpi-strip.tsx diff --git a/apps/web/src/design-preview/kpi.tsx b/apps/web/src/components/portal/kpi.tsx similarity index 100% rename from apps/web/src/design-preview/kpi.tsx rename to apps/web/src/components/portal/kpi.tsx diff --git a/apps/web/src/design-preview/section-grid.tsx b/apps/web/src/components/portal/section-grid.tsx similarity index 99% rename from apps/web/src/design-preview/section-grid.tsx rename to apps/web/src/components/portal/section-grid.tsx index c1d5434..84bf282 100644 --- a/apps/web/src/design-preview/section-grid.tsx +++ b/apps/web/src/components/portal/section-grid.tsx @@ -32,8 +32,8 @@ import { Switch } from '@authportal/ui/components/switch' import { Tabs, TabsList, TabsTrigger } from '@authportal/ui/components/tabs' import { cn } from '@authportal/ui/lib/utils' import { StatusBadge } from './bits' -import type { Tone } from './mock' -import type { RowMark } from './preview-store' +import type { Tone } from './tone' +import type { RowMark } from './tone' import { TONE_DOT } from './tone' export type GridColumn = { @@ -83,7 +83,7 @@ const ACTIONS_COLUMN_SIZE = 120 * сохранённые виды линией со счётчиками, панель массовых действий, таблица со статусом и меню строки, * подвал «Показано N из M». Ширина таблицы не меньше суммы колонок — прокрутка внутри контейнера. */ -export function PreviewSectionGrid({ +export function SectionGrid({ title, description, rows, diff --git a/apps/web/src/design-preview/tone.ts b/apps/web/src/components/portal/tone.ts similarity index 71% rename from apps/web/src/design-preview/tone.ts rename to apps/web/src/components/portal/tone.ts index 5a13a3c..b45a9d4 100644 --- a/apps/web/src/design-preview/tone.ts +++ b/apps/web/src/components/portal/tone.ts @@ -1,4 +1,8 @@ -import type { Tone } from './mock' +export type Tone = 'ok' | 'wn' | 'er' | 'mu' | 'in' + +/** Пометка строки с накопленной правкой. */ +export type RowMark = 'new' | 'dirty' | 'deleted' + type BadgeVariant = | 'success-light' diff --git a/apps/web/src/design-preview/access-editor.tsx b/apps/web/src/design-preview/access-editor.tsx index b0ae75f..afb7e0c 100644 --- a/apps/web/src/design-preview/access-editor.tsx +++ b/apps/web/src/design-preview/access-editor.tsx @@ -10,7 +10,7 @@ import { } from '@authportal/ui/components/select' import { Switch } from '@authportal/ui/components/switch' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs' -import { CodeBlock, SignalAlert } from './bits' +import { CodeBlock, SignalAlert } from '@/components/portal/bits' import { withLevel, levelOf, type AccessValue, type Level } from './access-model' import { APP_SHORT, USER_JOURNAL } from './mock' diff --git a/apps/web/src/design-preview/access-matrix.tsx b/apps/web/src/design-preview/access-matrix.tsx index 3d71e3a..ae6d79e 100644 --- a/apps/web/src/design-preview/access-matrix.tsx +++ b/apps/web/src/design-preview/access-matrix.tsx @@ -1,6 +1,6 @@ -import { StatusBadge } from './bits' +import { StatusBadge } from '@/components/portal/bits' import type { Tone } from './mock' -import { PreviewSectionGrid, TwoLineCell, type GridColumn } from './section-grid' +import { SectionGrid, TwoLineCell, type GridColumn } from '@/components/portal/section-grid' type MatrixRow = { id: string @@ -39,7 +39,7 @@ const COLUMNS: GridColumn[] = [ /** Матрица доступа по доске: сколько пользователей имеет доступ к каждому приложению и с каким уровнем. */ export function AccessMatrix() { return ( - diffAccess({ apps: user.apps, permissions: user.permissions }, value).length, [user, value]) return ( - !o && onClose()} size="lg" @@ -47,6 +47,6 @@ export function AccessSheet({ } > - + ) } diff --git a/apps/web/src/design-preview/create-sheets.tsx b/apps/web/src/design-preview/create-sheets.tsx index b86c015..4c5aa32 100644 --- a/apps/web/src/design-preview/create-sheets.tsx +++ b/apps/web/src/design-preview/create-sheets.tsx @@ -6,10 +6,10 @@ import { Field, FieldDescription, FieldError, FieldLabel } from '@authportal/ui/ import { Input } from '@authportal/ui/components/input' import { Switch } from '@authportal/ui/components/switch' import { Textarea } from '@authportal/ui/components/textarea' -import { PreviewSheet, SignalAlert } from './bits' +import { PortalSheet, SignalAlert } from '@/components/portal/bits' /** Окно «Создать пользователя» по доске: email, имя, временный пароль, администратор и заметка про журнал. */ -export function CreateUserPreviewSheet({ +export function CreateUserPortalSheet({ open, onOpenChange, onSubmit, @@ -37,7 +37,7 @@ export function CreateUserPreviewSheet({ } return ( - { if (!o) reset() @@ -93,12 +93,12 @@ export function CreateUserPreviewSheet({ Создание записывается в журнал «Изменения» с вашим именем и IP. - + ) } /** Окно «Создать OIDC-клиент» по доске: название, redirect URI, включён. */ -export function CreateOidcPreviewSheet({ +export function CreateOidcPortalSheet({ open, onOpenChange, onSubmit, @@ -128,7 +128,7 @@ export function CreateOidcPreviewSheet({ } return ( - { if (!o) reset() @@ -189,6 +189,6 @@ export function CreateOidcPreviewSheet({ Выключенный клиент не может получить токены - + ) } diff --git a/apps/web/src/design-preview/mock.ts b/apps/web/src/design-preview/mock.ts index af1e998..c26cf0f 100644 --- a/apps/web/src/design-preview/mock.ts +++ b/apps/web/src/design-preview/mock.ts @@ -21,7 +21,8 @@ import { type AuditLogEntry, } from '@authportal/shared' -export type Tone = 'ok' | 'wn' | 'er' | 'mu' | 'in' +import type { Tone } from '@/components/portal/tone' +export type { Tone } export type NavItem = { id: string diff --git a/apps/web/src/design-preview/pages/app-links-page.tsx b/apps/web/src/design-preview/pages/app-links-page.tsx index e1a6e74..9ce560d 100644 --- a/apps/web/src/design-preview/pages/app-links-page.tsx +++ b/apps/web/src/design-preview/pages/app-links-page.tsx @@ -15,13 +15,13 @@ import { } from '@authportal/ui/components/select' import { Switch } from '@authportal/ui/components/switch' import { Tabs, TabsContent } from '@authportal/ui/components/tabs' -import { KpiStrip } from '../kpi-strip' -import { PageHead, PreviewSheet, StatusBadge, PageTab, PageTabsList, FormFrame, SettingRow } from '../bits' -import { AppCards } from '../canvas-blocks' -import { kpi } from '../kpi' +import { KpiStrip } from '@/components/portal/kpi-strip' +import { PageHead, PortalSheet, StatusBadge, PageTab, PageTabsList, FormFrame, SettingRow } from '@/components/portal/bits' +import { AppCards } from '@/components/portal/canvas-blocks' +import { kpi } from '@/components/portal/kpi' import { MOCK_SWITCHER } from '../mock' import { usePreviewStore } from '../preview-store' -import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid' +import { SectionGrid, TwoLineCell, type GridColumn } from '@/components/portal/section-grid' type LinkRow = { id: AppId @@ -180,7 +180,7 @@ export function AppLinksPage() { - - !o && setEditing(null)} title="Приложение" @@ -323,7 +323,7 @@ export function AppLinksPage() { ) : null} - + ) } diff --git a/apps/web/src/design-preview/pages/apps-page.tsx b/apps/web/src/design-preview/pages/apps-page.tsx index d63dc51..7603fd3 100644 --- a/apps/web/src/design-preview/pages/apps-page.tsx +++ b/apps/web/src/design-preview/pages/apps-page.tsx @@ -2,12 +2,12 @@ import { Link } from '@tanstack/react-router' import { BadgeCheckIcon, CloudIcon, GlobeIcon, KeyRoundIcon, LayoutDashboardIcon, LayoutGridIcon, ServerIcon, ShieldIcon } from 'lucide-react' import { toast } from 'sonner' import { APPS, type AppId } from '@authportal/shared' -import { KpiStrip } from '../kpi-strip' +import { KpiStrip } from '@/components/portal/kpi-strip' import { Button } from '@authportal/ui/components/button' import { Tabs, TabsContent } from '@authportal/ui/components/tabs' -import { PageHead, PageTab, PageTabsList } from '../bits' -import { AppCards, type AppCardItem } from '../canvas-blocks' -import { kpi } from '../kpi' +import { PageHead, PageTab, PageTabsList } from '@/components/portal/bits' +import { AppCards, type AppCardItem } from '@/components/portal/canvas-blocks' +import { kpi } from '@/components/portal/kpi' const ICONS: Record = { cfdm: CloudIcon, diff --git a/apps/web/src/design-preview/pages/audit-page.tsx b/apps/web/src/design-preview/pages/audit-page.tsx index 0b27549..fb60db3 100644 --- a/apps/web/src/design-preview/pages/audit-page.tsx +++ b/apps/web/src/design-preview/pages/audit-page.tsx @@ -1,16 +1,16 @@ import { HistoryIcon, NetworkIcon, OctagonAlertIcon, UserCogIcon } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' -import { KpiStrip } from '../kpi-strip' +import { KpiStrip } from '@/components/portal/kpi-strip' import { Button } from '@authportal/ui/components/button' import { Input } from '@authportal/ui/components/input' import { Switch } from '@authportal/ui/components/switch' import { Tabs, TabsContent } from '@authportal/ui/components/tabs' -import { CodeBlock, FormFrame, KvList, PageHead, PageTab, PageTabsList, PreviewSheet, SettingRow, StatusBadge, TypedConfirm } from '../bits' -import { ActivityFeed } from '../canvas-blocks' -import { kpi } from '../kpi' +import { CodeBlock, FormFrame, KvList, PageHead, PageTab, PageTabsList, PortalSheet, SettingRow, StatusBadge, TypedConfirm } from '@/components/portal/bits' +import { ActivityFeed } from '@/components/portal/canvas-blocks' +import { kpi } from '@/components/portal/kpi' import { AUDIT_FEED, AUDIT_ROWS, type AuditRow } from '../mock' -import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid' +import { SectionGrid, TwoLineCell, type GridColumn } from '@/components/portal/section-grid' const mono = (v: string) => {v} @@ -74,7 +74,7 @@ export function AuditPage() { /> - - !o && setEntry(null)} title="Событие журнала" @@ -137,7 +137,7 @@ export function AuditPage() { ) : null} - + - - - !o && setEvent(null)} title="Событие входа" @@ -148,7 +148,7 @@ export function LoginsPage() { {`action: ${event.tone === 'er' ? 'auth.login_failed' : 'auth.login'}\nsource_app: portal\nseverity: ${event.tone === 'er' ? 'warning' : 'info'}\nip: ${event.ip}`} ) : null} - + ) } diff --git a/apps/web/src/design-preview/pages/oidc-page.tsx b/apps/web/src/design-preview/pages/oidc-page.tsx index 1d509ef..93907e0 100644 --- a/apps/web/src/design-preview/pages/oidc-page.tsx +++ b/apps/web/src/design-preview/pages/oidc-page.tsx @@ -2,8 +2,8 @@ import { useMemo, useState } from 'react' import { CircleCheckIcon, CopyIcon, KeyRoundIcon, LockKeyholeIcon, TicketIcon } from 'lucide-react' import { toast } from 'sonner' import type { CreateOidcClientValues } from '@/components/reui-kit/create-oidc-client-sheet' -import { CreateOidcPreviewSheet } from '../create-sheets' -import { KpiStrip } from '../kpi-strip' +import { CreateOidcPortalSheet } from '../create-sheets' +import { KpiStrip } from '@/components/portal/kpi-strip' import { Button } from '@authportal/ui/components/button' import { AlertDialog, @@ -18,9 +18,9 @@ import { Input } from '@authportal/ui/components/input' import { Switch } from '@authportal/ui/components/switch' import { Tabs, TabsContent } from '@authportal/ui/components/tabs' import { Textarea } from '@authportal/ui/components/textarea' -import { ExplainBanner } from '../explain-banner' -import { CopyList, KvFrame, PageHead, PreviewSheet, StatusBadge, TypedConfirm, PageTab, PageTabsList } from '../bits' -import { kpi } from '../kpi' +import { ExplainBanner } from '@/components/portal/explain-banner' +import { CopyList, KvFrame, PageHead, PortalSheet, StatusBadge, TypedConfirm, PageTab, PageTabsList } from '@/components/portal/bits' +import { kpi } from '@/components/portal/kpi' import { MOCK_CLIENT_SECRET, MOCK_OIDC_CLIENTS, @@ -29,7 +29,7 @@ import { type OidcClientRow, type Tone } from '../mock' -import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid' +import { SectionGrid, TwoLineCell, type GridColumn } from '@/components/portal/section-grid' function tone(c: OidcClientRow): Tone { return !c.enabled ? 'mu' : c.stale ? 'wn' : 'ok' @@ -111,7 +111,7 @@ export function OidcPage() { - - + - !o && setEditing(null)} title="OIDC-клиент" @@ -193,7 +193,7 @@ export function OidcPage() { ) : null} - + (MOCK_PASSKEYS) @@ -72,7 +72,7 @@ export function SecurityPage() { - - Откроется системный диалог: подтвердите отпечатком, Face ID, PIN или ключом безопасности. Закрытый ключ не покидает устройство. - + - !o && setSelected(null)} title="Passkey" @@ -213,7 +213,7 @@ export function SecurityPage() { ['Последний вход', `${selected?.lastUsed ?? ''} · 203.0.113.10`], ]} /> - + - + {accessFor ? (