From 1b89135186c363ffdd2a6b32a4334f757794dd74 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 18 Oct 2026 03:44:19 +0700 Subject: [PATCH] =?UTF-8?q?fix(design-preview):=20=D0=BE=D0=BA=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F,=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=BF=D0=B8=D1=81=D0=B8=20=D0=B8=20=D0=BA=D0=BD?= =?UTF-8?q?=D0=BE=D0=BF=D0=BA=D0=B8=20=D0=BA=D0=B0=D0=BA=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D0=BA=D0=B0=D1=85=20Auth=20Portal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Окна «Создать пользователя» и «Создать OIDC-клиент» по доске с проверкой полей; баннер безопасности всегда виден; кнопки действий без иконок; подписи иконок приложений как на доске; поиск в таблице passkeys. Co-Authored-By: Claude Sonnet 5.5 --- apps/web/src/design-preview/create-sheets.tsx | 194 ++++++++++++++++++ .../design-preview/pages/app-links-page.tsx | 9 +- .../src/design-preview/pages/audit-page.tsx | 2 - .../src/design-preview/pages/logins-page.tsx | 2 - .../src/design-preview/pages/oidc-page.tsx | 9 +- .../design-preview/pages/security-page.tsx | 10 +- .../src/design-preview/pages/users-page.tsx | 6 +- apps/web/src/design-preview/tone.ts | 2 +- 8 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 apps/web/src/design-preview/create-sheets.tsx diff --git a/apps/web/src/design-preview/create-sheets.tsx b/apps/web/src/design-preview/create-sheets.tsx new file mode 100644 index 0000000..b86c015 --- /dev/null +++ b/apps/web/src/design-preview/create-sheets.tsx @@ -0,0 +1,194 @@ +import { useState } from 'react' +import type { CreateUserRequest } from '@authportal/shared' +import type { CreateOidcClientValues } from '@/components/reui-kit/create-oidc-client-sheet' +import { Button } from '@authportal/ui/components/button' +import { Field, FieldDescription, FieldError, FieldLabel } from '@authportal/ui/components/field' +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' + +/** Окно «Создать пользователя» по доске: email, имя, временный пароль, администратор и заметка про журнал. */ +export function CreateUserPreviewSheet({ + open, + onOpenChange, + onSubmit, +}: { + open: boolean + onOpenChange: (open: boolean) => void + onSubmit: (values: CreateUserRequest) => void +}) { + const [email, setEmail] = useState('') + const [name, setName] = useState('') + const [password, setPassword] = useState('') + const [admin, setAdmin] = useState(false) + const [touched, setTouched] = useState(false) + + const passwordError = password.length < 12 ? 'Минимум 12 символов' : null + const emailError = !/^\S+@\S+\.\S+$/.test(email) ? 'Укажите email' : null + const valid = !passwordError && !emailError + + function reset() { + setEmail('') + setName('') + setPassword('') + setAdmin(false) + setTouched(false) + } + + return ( + { + if (!o) reset() + onOpenChange(o) + }} + dirty={Boolean(email || name || password || admin)} + title="Создать пользователя" + description="После создания откроются права доступа" + footer={ + <> + + + + } + > + + + Email * + + setEmail(e.target.value)} /> + {touched && emailError ? {emailError} : Станет логином} + + + Имя + setName(e.target.value)} /> + + + + Временный пароль * + + setPassword(e.target.value)} /> + {touched && passwordError ? ✕ {passwordError} : Пользователь заменит его на passkey} + + + +
+ Администратор + Полный доступ к порталу и ко всем приложениям +
+
+ + Создание записывается в журнал «Изменения» с вашим именем и IP. + +
+ ) +} + +/** Окно «Создать OIDC-клиент» по доске: название, redirect URI, включён. */ +export function CreateOidcPreviewSheet({ + open, + onOpenChange, + onSubmit, +}: { + open: boolean + onOpenChange: (open: boolean) => void + onSubmit: (values: CreateOidcClientValues) => void +}) { + const [name, setName] = useState('') + const [uris, setUris] = useState('') + const [enabled, setEnabled] = useState(true) + const [touched, setTouched] = useState(false) + + const list = uris + .split('\n') + .map((x) => x.trim()) + .filter(Boolean) + const urisError = list.length === 0 || list.some((u) => !/^https:\/\/[^\s*]+$/.test(u)) ? 'Укажите точный https-адрес колбэка приложения.' : null + const nameError = name.trim() ? null : 'Введите название' + const valid = !urisError && !nameError + + function reset() { + setName('') + setUris('') + setEnabled(true) + setTouched(false) + } + + return ( + { + if (!o) reset() + onOpenChange(o) + }} + dirty={Boolean(name || uris)} + title="Создать OIDC-клиент" + description="Секрет будет показан один раз после создания" + footer={ + <> + + + + } + > + + + Название * + + setName(e.target.value)} /> + {touched && nameError ? {nameError} : null} + + + + Redirect URI * + +