Frontend:
- apps/web (Vite+TS, TanStack Router/Query, shadcn/ui @cfdm/ui base-nova)
- 10 страниц в routes/_auth/, Recharts через shadcn Chart, lucide-react
- формы на RHF + Zod (FormSheet/FormField)
- удалены Tabler, Chart.js, react-router-dom
Backend (параллельный трек):
- apps/api (Fastify 5 + Drizzle + better-sqlite3)
- packages/db: Drizzle-схема и repositories по сущностям
- packages/shared: Zod-контракты
- роуты с валидацией и единым форматом ошибок { error: { code, message } }
- sync/backup — заглушки 501 (billmanager-адаптеры переносятся отдельно)
- legacy Express оставлен как runtime по умолчанию (RUNTIME=express)
Infra:
- Dockerfile multi-stage под pnpm workspaces
- .dockerignore и docker-compose обновлены под monorepo
Rules:
- удалены нерелевантные правила (rust, cloudflare, server/frontend-conventions)
- project-structure.mdc и AGENTS.md переписаны под monorepo
- frontend-shadcn.mdc, shadcn-ui-production.mdc, sqlite.mdc обновлены
Co-authored-by: Cursor <cursoragent@cursor.com>
112 lines
4.4 KiB
Plaintext
112 lines
4.4 KiB
Plaintext
---
|
||
description: Единые UI-паттерны web — shared components, docs workflow, матрица стандартизации
|
||
globs: apps/web/**/*
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Frontend UI Patterns
|
||
|
||
См. также: [`frontend-shadcn.mdc`](frontend-shadcn.mdc), [`shadcn-mcp.mdc`](shadcn-mcp.mdc), [Components](https://ui.shadcn.com/docs/components), [Blocks](https://ui.shadcn.com/blocks).
|
||
|
||
## Docs workflow (обязательно)
|
||
|
||
0. Codegraph `codegraph_explore` — найти существующие shared/domain-компоненты
|
||
1. Skill [`.agents/skills/shadcn/SKILL.md`](../.agents/skills/shadcn/SKILL.md) — component selection
|
||
2. MCP `plugin-shadcn-shadcn` — search → examples → add command
|
||
3. CLI: `cd apps/web && pnpm dlx shadcn@latest docs <component>` → сверить API с [Components](https://ui.shadcn.com/docs/components)
|
||
4. Код по examples + docs API (только после совпадения MCP ↔ docs)
|
||
5. Context7 — **только** TanStack Router/Query, Recharts (не shadcn primitives)
|
||
6. MCP `get_audit_checklist` — перед merge
|
||
7. Codegraph `codegraph_status` — Pending sync пустой
|
||
|
||
## Иерархия компонентов
|
||
|
||
```
|
||
@cfdm/ui/components/* ← только CLI (packages/ui)
|
||
apps/web/src/components/ ← shared + domain + layout
|
||
page-shell.tsx ← обёртка страницы
|
||
page-header.tsx
|
||
empty-state.tsx
|
||
query-state.tsx
|
||
confirm-dialog.tsx
|
||
data-table-card.tsx
|
||
section-cards.tsx
|
||
status-badge.tsx
|
||
form-sheet.tsx ← Sheet + RHF FormProvider
|
||
form-field.tsx ← Field + Controller + aria-invalid
|
||
table-card.tsx ← Card + Table wrapper
|
||
loading-button.tsx ← Button + Spinner + label swap
|
||
section-cards-skeleton.tsx
|
||
table-skeleton.tsx
|
||
layout/ ← app-shell, site-header
|
||
domain-* ← бизнес-компоненты
|
||
```
|
||
|
||
## Матрица стандартизации
|
||
|
||
| Элемент | Shared | Primitive |
|
||
|---------|--------|-----------|
|
||
| Page wrapper | `PageShell` | — |
|
||
| Page title | `PageHeader` | — |
|
||
| Stat metrics | `SectionCards` | `Card` |
|
||
| Data list | `DataTableCard` | `Table`, `InputGroup` |
|
||
| Empty | `EmptyState` | `Empty` |
|
||
| Loading / Error | `QueryState` | `Skeleton`, `Alert` |
|
||
| Status | `StatusBadge` | `Badge` |
|
||
| Create/Edit | `FormSheet` + `*-edit-sheet.tsx` | `Sheet`, `Field` |
|
||
| Form field | `FormField` | `Field`, `Input`, `Select` |
|
||
| Submit button | `LoadingButton` | `Button`, `Spinner` |
|
||
| Table wrapper | `TableCard` | `Table`, `Card` |
|
||
| Delete confirm | `ConfirmDialog` | `AlertDialog` |
|
||
| List row | — | `Item variant="outline" size="sm"` |
|
||
| Nav | `AppSidebar` | `Sidebar` |
|
||
| Breadcrumbs | `SiteHeader` | `Breadcrumb` |
|
||
| Dates | `lib/format.ts` | — |
|
||
|
||
## Overlay selection
|
||
|
||
| Сценарий | Компонент |
|
||
|----------|-----------|
|
||
| Create/edit форма | `Sheet` |
|
||
| Destructive confirm | `AlertDialog` via `ConfirmDialog` |
|
||
| Modal preview | `Dialog` |
|
||
|
||
## Block registry
|
||
|
||
| Зона | Block |
|
||
|------|-------|
|
||
| Shell | sidebar-07 |
|
||
| Dashboard | dashboard-01 |
|
||
| Login | login-03 |
|
||
|
||
## Spacing
|
||
|
||
```
|
||
PageShell: gap-4 md:gap-6, px-4 lg:px-6 py-4 md:py-6
|
||
Card grid: gap-4
|
||
FieldGroup: gap-4
|
||
Item list: gap-2
|
||
Toolbar: gap-2
|
||
```
|
||
|
||
**Запрещено:** `space-y-*`, raw colors (`bg-emerald-*`), custom empty divs, page-level Spinner.
|
||
|
||
## UX/UI 2026 (состояния данных)
|
||
|
||
Каждый блок: **default, hover, focus, disabled, empty, loading, error**.
|
||
|
||
- **Loading** — `Skeleton` с размерами финального контента (`QueryState skeleton={…}`), не Spinner на странице
|
||
- **Empty** — `EmptyState` с CTA (кнопка создания)
|
||
- **Zero-results** — отдельный empty с «Сбросить фильтр» (не «Создать»)
|
||
- **Error** — `QueryState` + `onRetry` + иконка + текст
|
||
- **Overflow** — `truncate`, `max-w-*`, `Tooltip`; `tabular-nums` для чисел
|
||
- **Density** — таблицы `h-10 text-sm`; max 1 primary CTA на экран
|
||
- **A11y** — `aria-invalid` на полях, `aria-label`/`sr-only` на icon-only кнопках, цвет не единственный сигнал статуса
|
||
|
||
## Button hierarchy (max 1 primary per screen)
|
||
|
||
1. `default` — главный CTA
|
||
2. `outline` — вторичные действия
|
||
3. `ghost` / `link` — навигация, cancel
|
||
4. `destructive` — только с `ConfirmDialog`
|