fix(design-preview): убрать блоки «Состояния раздела»
Блоки были переносом из канваса и не несли пользы на страницах макета. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
59d68a3a45
commit
7f9d69d27d
8 files changed
+7
-113
No files matched your search
@@ -301,51 +301,6 @@ export function CopyList({
|
||||
)
|
||||
}
|
||||
|
||||
export type StateItem = {
|
||||
title: string
|
||||
when: string
|
||||
text: string
|
||||
tone?: 'wn' | 'er'
|
||||
cta: string
|
||||
}
|
||||
|
||||
/** Пустые и аварийные состояния раздела (как «Состояния раздела» канваса). */
|
||||
export function StatesFrame({
|
||||
title,
|
||||
description,
|
||||
items,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
items: StateItem[]
|
||||
}) {
|
||||
return (
|
||||
<Frame spacing="sm" className="w-full">
|
||||
<FrameHeader>
|
||||
<FrameTitle>{title}</FrameTitle>
|
||||
<FrameDescription>{description}</FrameDescription>
|
||||
</FrameHeader>
|
||||
<FramePanel className="@container p-0">
|
||||
<ul className="grid @2xl:grid-cols-2">
|
||||
{items.map((item) => (
|
||||
<li key={item.title} className="flex flex-col items-start gap-2 border-b px-4 py-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-sm font-medium">{item.title}</span>
|
||||
<StatusBadge tone={item.tone ?? 'mu'}>{item.tone === 'er' ? 'Ошибка' : item.tone === 'wn' ? 'Внимание' : 'Пусто'}</StatusBadge>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-xs">Когда: {item.when}</p>
|
||||
<p className="text-sm text-pretty">{item.text}</p>
|
||||
<Button type="button" size="sm" variant="outline" onClick={() => toast(item.cta)}>
|
||||
{item.cta}
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</FramePanel>
|
||||
</Frame>
|
||||
)
|
||||
}
|
||||
|
||||
/** Компактный список «ключ — значение» для боковых окон. */
|
||||
export function KvList({ items }: { items: Array<[string, string]> }) {
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Field, FieldDescription, FieldLabel } from '@authportal/ui/components/f
|
||||
import { Input } from '@authportal/ui/components/input'
|
||||
import { Switch } from '@authportal/ui/components/switch'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { PageHead, PreviewSheet, StatesFrame, StatusBadge } from '../bits'
|
||||
import { PageHead, PreviewSheet, StatusBadge } from '../bits'
|
||||
import { MOCK_SWITCHER } from '../mock'
|
||||
import { usePreviewStore } from '../preview-store'
|
||||
import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid'
|
||||
@@ -174,15 +174,6 @@ export function AppLinksPage() {
|
||||
)}
|
||||
onRowClick={setEditing}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния раздела"
|
||||
description="Ошибки адреса, пустое меню и сбой сохранения"
|
||||
items={[
|
||||
{ title: 'Некорректный URL', when: 'адрес не https или не входит в allowlist return_to', text: 'Укажите https-адрес из доверенного домена (.shnt.top).', tone: 'wn', cta: 'Исправить' },
|
||||
{ title: 'Меню пусто', when: 'все приложения скрыты', text: 'В переключателе не останется ни одного приложения — оставьте хотя бы одно.', tone: 'wn', cta: 'Показать приложения' },
|
||||
{ title: 'Не удалось сохранить', when: 'PUT /api/v1/admin/app-switcher вернул ошибку', text: 'Изменения не записаны. Повторите сохранение.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="menu">
|
||||
<AppSwitcherAdminEditor
|
||||
|
||||
@@ -12,7 +12,7 @@ import { KpiStatGrid } from '@/components/reui-kit/kpi-stat-grid'
|
||||
import { QuickActionGrid } from '@/components/reui-kit/quick-action-grid'
|
||||
import { Button } from '@authportal/ui/components/button'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { PageHead, StatesFrame } from '../bits'
|
||||
import { PageHead } from '../bits'
|
||||
|
||||
const ICONS: Record<AppId, typeof CloudIcon> = {
|
||||
cfdm: CloudIcon,
|
||||
@@ -72,15 +72,6 @@ export function AppsPage() {
|
||||
}
|
||||
})}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния раздела"
|
||||
description="Пустой список, сбой SSO, недоступный каталог"
|
||||
items={[
|
||||
{ title: 'Нет доступных приложений', when: 'администратор ещё не выдал права', text: 'Обратитесь к администратору, чтобы получить доступ.', cta: 'Написать администратору' },
|
||||
{ title: 'SSO не удался', when: 'не удалось выпустить токен для приложения', text: 'Открываем приложение по базовому адресу — войдите в нём отдельно. Портал записал событие в журнал.', tone: 'wn', cta: 'Повторить SSO' },
|
||||
{ title: 'Каталог недоступен', when: 'GET /api/v1/catalog вернул ошибку', text: 'Не удалось загрузить список приложений. Повторите запрос.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="no">
|
||||
<QuickActionGrid
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AuditRetentionPanel } from '@/components/reui-kit/audit-retention-panel
|
||||
import { KpiStatGrid } from '@/components/reui-kit/kpi-stat-grid'
|
||||
import { Button } from '@authportal/ui/components/button'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { Segmented, CodeBlock, KvList, PageHead, PreviewSheet, StatesFrame, StatusBadge, TypedConfirm } from '../bits'
|
||||
import { Segmented, CodeBlock, KvList, PageHead, PreviewSheet, StatusBadge, TypedConfirm } from '../bits'
|
||||
import { MOCK_AUDIT, APP_SHORT, type Tone } from '../mock'
|
||||
import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid'
|
||||
|
||||
@@ -106,14 +106,6 @@ export function AuditPage() {
|
||||
<TabsContent value="all" className="flex min-w-0 flex-col gap-4">
|
||||
<AuditLogTimeline entries={MOCK_AUDIT} totalCount={MOCK_AUDIT.length} />
|
||||
{grid('Журнал изменений', 'Поиск по событию, пользователю и IP · фильтры: источник, важность, объект', MOCK_AUDIT)}
|
||||
<StatesFrame
|
||||
title="Состояния журнала"
|
||||
description="Пустые и аварийные состояния"
|
||||
items={[
|
||||
{ title: 'Нет событий', when: 'за выбранный период записей нет', text: 'За выбранный период в журнале нет записей.', cta: 'Сменить период' },
|
||||
{ title: 'Журнал недоступен', when: 'запрос журнала завершился ошибкой', text: 'Не удалось загрузить журнал. Повторите запрос.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="auth">{grid('События аутентификации', 'Входы, выходы и SSO · подробности — в разделе «Входы»', auth)}</TabsContent>
|
||||
<TabsContent value="users">{grid('Изменения пользователей', 'Создание, правки, права и passkeys', users)}</TabsContent>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { toast } from 'sonner'
|
||||
import { KpiStatGrid } from '@/components/reui-kit/kpi-stat-grid'
|
||||
import { Button } from '@authportal/ui/components/button'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { Segmented, CodeBlock, KvList, PageHead, PreviewSheet, SignalAlert, StatesFrame, StatusBadge } from '../bits'
|
||||
import { Segmented, CodeBlock, KvList, PageHead, PreviewSheet, SignalAlert, StatusBadge } from '../bits'
|
||||
import { MOCK_LOGINS, MOCK_SESSIONS, type LoginRow, type SessionRow } from '../mock'
|
||||
import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid'
|
||||
|
||||
@@ -114,14 +114,6 @@ export function LoginsPage() {
|
||||
]}
|
||||
onRowClick={setEvent}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния журнала"
|
||||
description="Пустые и аварийные состояния"
|
||||
items={[
|
||||
{ title: 'Нет событий', when: 'за выбранный период входов не было', text: 'За выбранный период входов не было.', cta: 'Сменить период' },
|
||||
{ title: 'Журнал недоступен', when: 'запрос журнала входов завершился ошибкой', text: 'Не удалось загрузить журнал. Повторите запрос.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="sess" className="flex min-w-0 flex-col gap-4">
|
||||
<PreviewSectionGrid
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Input } from '@authportal/ui/components/input'
|
||||
import { Switch } from '@authportal/ui/components/switch'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { Textarea } from '@authportal/ui/components/textarea'
|
||||
import { CopyList, KvFrame, PageHead, PreviewSheet, SignalAlert, StatesFrame, StatusBadge, TypedConfirm } from '../bits'
|
||||
import { CopyList, KvFrame, PageHead, PreviewSheet, SignalAlert, StatusBadge, TypedConfirm } from '../bits'
|
||||
import {
|
||||
MOCK_CLIENT_SECRET,
|
||||
MOCK_OIDC_CLIENTS,
|
||||
@@ -140,15 +140,6 @@ export function OidcPage() {
|
||||
]}
|
||||
onRowClick={setEditing}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния раздела"
|
||||
description="Пустой список, ошибки redirect URI и метаданных"
|
||||
items={[
|
||||
{ title: 'Нет OIDC-клиентов', when: 'ни одно внешнее приложение не зарегистрировано', text: 'Создайте клиента для Technitium DNS или другого OIDC-приложения.', cta: 'Создать клиента' },
|
||||
{ title: 'Ошибка redirect URI', when: 'redirect URI не https или содержит wildcard', text: 'Укажите точный https-адрес колбэка приложения.', tone: 'wn', cta: 'Исправить' },
|
||||
{ title: 'Метаданные недоступны', when: 'GET /api/v1/admin/oidc/meta вернул ошибку', text: 'Не удалось загрузить параметры issuer. Повторите запрос.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="meta" className="flex min-w-0 flex-col gap-4">
|
||||
<CopyList title="Параметры для подключения приложения" description="Скопируйте в настройки OIDC внешнего приложения" items={OIDC_ENDPOINTS} />
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Field, FieldDescription, FieldGroup, FieldLabel } from '@authportal/ui/
|
||||
import { Input } from '@authportal/ui/components/input'
|
||||
import { Switch } from '@authportal/ui/components/switch'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@authportal/ui/components/tabs'
|
||||
import { KvFrame, KvList, PageHead, PreviewSheet, SignalAlert, StatesFrame, StatusBadge, TypedConfirm } from '../bits'
|
||||
import { KvFrame, KvList, PageHead, PreviewSheet, SignalAlert, StatusBadge, TypedConfirm } from '../bits'
|
||||
import { MOCK_PASSKEYS, type PasskeyRow } from '../mock'
|
||||
import { PreviewSectionGrid, TwoLineCell, type GridColumn } from '../section-grid'
|
||||
|
||||
@@ -80,15 +80,6 @@ export function SecurityPage() {
|
||||
setRenameValue(r.name)
|
||||
}}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния раздела"
|
||||
description="Пустой список и сбои WebAuthn"
|
||||
items={[
|
||||
{ title: 'Ключи не зарегистрированы', when: 'у пользователя нет ни одного passkey', text: 'Добавьте passkey, чтобы входить без пароля.', cta: 'Добавить passkey' },
|
||||
{ title: 'Браузер не поддерживает passkeys', when: 'WebAuthn недоступен (HTTP или старый браузер)', text: 'Откройте портал по HTTPS в современном браузере. Вход по паролю остаётся доступным.', tone: 'wn', cta: 'Понятно' },
|
||||
{ title: 'Регистрация отменена', when: 'системный диалог закрыт или истёк таймаут', text: 'Passkey не добавлен. Повторите попытку.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="pass">
|
||||
<Frame spacing="sm" className="w-full">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { KpiStatGrid } from '@/components/reui-kit/kpi-stat-grid'
|
||||
import { Button } from '@authportal/ui/components/button'
|
||||
import { AccessDialog } from '../access-dialog'
|
||||
import { diffAccess, type AccessValue } from '../access-model'
|
||||
import { CodeBlock, KvList, PageHead, PreviewSheet, SignalAlert, StatesFrame, TypedConfirm } from '../bits'
|
||||
import { CodeBlock, KvList, PageHead, PreviewSheet, SignalAlert, TypedConfirm } from '../bits'
|
||||
import { MOCK_USERS, USER_JOURNAL } from '../mock'
|
||||
import { usePreviewStore } from '../preview-store'
|
||||
|
||||
@@ -143,15 +143,6 @@ export function UsersPage() {
|
||||
onBulkSetRole={onBulkSetRole}
|
||||
onBulkDeactivate={onBulkDeactivate}
|
||||
/>
|
||||
<StatesFrame
|
||||
title="Состояния раздела"
|
||||
description="Пустые и аварийные состояния каталога"
|
||||
items={[
|
||||
{ title: 'Нет пользователей', when: 'каталог пуст — первый вход ещё не выполнен', text: 'Создайте первого пользователя и выдайте ему доступ к приложениям.', cta: 'Создать пользователя' },
|
||||
{ title: 'Нельзя отключить себя', when: 'попытка отключить собственную запись или последнего администратора', text: 'В портале должен остаться хотя бы один активный администратор.', tone: 'wn', cta: 'Понятно' },
|
||||
{ title: 'Каталог недоступен', when: 'GET /api/v1/admin/users завершился ошибкой', text: 'Ошибка загрузки. Повторите запрос.', tone: 'er', cta: 'Повторить' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<CreateUserSheet open={createOpen} onOpenChange={setCreateOpen} pending={false} error={null} onSubmit={onCreate} />
|
||||
|
||||
|
||||
Reference in new issue
Block a user