docs: детализация дизайна Фазы 1C (React SPA)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 16:40:22 +07:00
parent b329855c28
commit fe85918407
@@ -233,3 +233,80 @@ check_runs(id uuid pk, domain_id uuid fk→domains, result jsonb /*сводка
### Разбивка ### Разбивка
Реализуется **одним планом** `phase1b-persistence-api` (по решению — без под-планов). Реализуется **одним планом** `phase1b-persistence-api` (по решению — без под-планов).
## Фаза 1C — детализация (React SPA)
Строится поверх готового REST API (Фаза 1B). Полный UI на весь API.
### Стек
- **Vite + React + TypeScript**, **react-router** (v6+), **TanStack Query v5** (server-state).
- **Tailwind CSS + shadcn/ui** (компоненты копируются в проект, `components.json`, alias `@`).
- Формы: **react-hook-form + zod**.
- Тесты: **Vitest + React Testing Library**.
- В dev — Vite `server.proxy` `/api``http://localhost:8080`. В prod — `vite build``dist/`
вшивается в Go-бинарь (`embed.FS`) и отдаётся тем же сервером (same-origin, CORS не нужен).
### Эстетическое направление
**Refined technical console.** Тёмная тема по умолчанию, плотная точная сетка, инженерный тон.
- Типографика: характерный UI-гротеск (НЕ Inter/Roboto/system) + **моноширинный** для DNS-записей,
зон и дифов (например IBM Plex Mono / JetBrains Mono). Пары: display для заголовков, mono для данных.
- Семантическое цветовое кодирование дифа: **add** (emerald), **update** (amber), **delete/prune**
(rose), **in-sync** (muted/slate), **read-only NS/SOA** (dimmed). Единая палитра через CSS-переменные.
- Атмосфера: тонкие текстуры/границы, аккуратные тени, без generic purple-on-white. Диф — центральный,
самый выразительный экран.
### Структура (`web/`)
```
web/
vite.config.ts — alias @, proxy /api → :8080, build outDir dist
components.json — shadcn
src/
main.tsx, App.tsx — QueryClientProvider + router
lib/ — utils, константа DEFAULT_PROJECT_ID = seed …0002
api/
client.ts — типизированная fetch-обёртка (base /api/v1/projects/{pid})
types.ts — зеркало Go DTO (Account, Template, Domain, Changeset, RecordView …)
hooks/ — TanStack Query: useAccounts/useTemplates/useDomains/useCheck/useApply/useImport
components/
ui/ — shadcn-компоненты
Layout.tsx, DiffView.tsx, RecordEditor.tsx
pages/
DomainsPage, DomainDiffPage, AccountsPage, TemplatesPage
```
### Экраны
- **Domains** — список доменов, импорт зон по учётке (`POST /domains/import`), привязка шаблона
(`PATCH /domains/{id}`), переход к дифу.
- **DomainDiff** (`/domains/:id`) — `GET .../check`**DiffView**: секции Updates / Prunes / ReadOnly
+ счётчик in-sync. Кнопка **Apply**: чекбокс «применить удаления (prune)» — **по умолчанию выключен**,
требует явного подтверждения (визуально акцентированное предупреждение). `POST .../apply`.
- **Accounts** — CRUD учёток. Форма создания: поле API-ключа (secret, только на вход), **инструкция +
ссылка**, где получить ключ в панели Selectel. Секрет никогда не показывается обратно.
- **Templates** — CRUD шаблонов + **RecordEditor** (редактор набора записей `doc.records`: type/name/ttl/values,
включая SRV для почтового автодискавера).
### Мультитенантность
Фаза 1C — без логина: `DEFAULT_PROJECT_ID` (seed `…0002`) зашит в клиенте. Логин и переключение
проектов — Фаза 2 (тогда pid берётся из сессии).
### Подача в prod (Go)
- `internal/web/web.go``embed.FS` каталога `web/dist` + handler со **SPA-fallback** на `index.html`.
- `cmd/server` монтирует статику на `/`, API остаётся на `/api/v1` (роутинг chi: API-роуты имеют приоритет,
всё прочее → SPA).
### Тестирование 1C
- `api/client` — юниты на мок `fetch` (пути, сериализация, обработка ошибок).
- `DiffView` — рендер секций Updates/Prunes/ReadOnly, цветовое кодирование.
- Apply-guard — по умолчанию prune выключен; включается только явным действием.
- Go `internal/web` — статика отдаётся, `/api/*` не перехватывается, SPA-fallback на неизвестный путь.
### Разбивка
Один план `phase1c-react-spa`.