185 lines
6.8 KiB
Markdown
185 lines
6.8 KiB
Markdown
# VoIdea WebUI Style Guide
|
|
|
|
## Tech Stack
|
|
|
|
| Category | Choice | Version |
|
|
|----------|--------|---------|
|
|
| Framework | React | 18.3.x (LTS) |
|
|
| Language | TypeScript | 5.5+ (strict mode) |
|
|
| Routing | react-router-dom | 6.26.x |
|
|
| Styling | Tailwind CSS | 3.4.x |
|
|
| State | Zustand (new stores) + Context (existing) | 5.x |
|
|
| Forms | react-hook-form + zod | latest |
|
|
| i18n | react-i18next (future) / constants/strings.ts (now) | — |
|
|
| Testing | Vitest + @testing-library/react | latest |
|
|
| PWA | vite-plugin-pwa | 0.20.x |
|
|
| Build | Vite | 5.4.x |
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
webui/src/
|
|
├── api/ # HTTP client + endpoint modules
|
|
├── auth/ # Zustand store (future), Context (migration target)
|
|
├── components/ # Shared UI (ErrorBoundary, Layout, VoiceChat, etc.)
|
|
├── constants/ # strings.ts (i18n-ready), enums
|
|
├── hooks/ # Custom hooks
|
|
├── pages/ # Route pages (one file per route)
|
|
├── stores/ # Zustand stores (auth, ideas, settings...)
|
|
├── types/ # Shared TypeScript types
|
|
├── utils/ # Pure utility functions
|
|
├── App.tsx # Router setup
|
|
└── main.tsx # Entry point
|
|
```
|
|
|
|
## Coding Rules
|
|
|
|
### General
|
|
- `any` запрещён. Всегда явный тип.
|
|
- Импорты: абсолютные через `@/` alias. Относительные только для соседних файлов.
|
|
- Файл — не более 300 строк. Сервисы/компоненты больше — разбить.
|
|
- Prettier: 100 символов, двойные кавычки, точка с запятой.
|
|
|
|
### State Management (Zustand)
|
|
|
|
```ts
|
|
// stores/auth.ts
|
|
import { create } from "zustand";
|
|
|
|
interface AuthState {
|
|
user: User | null;
|
|
token: string | null;
|
|
login: (email: string, password: string) => Promise<void>;
|
|
logout: () => void;
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set) => ({
|
|
user: null,
|
|
token: null,
|
|
login: async (email, password) => { /* ... */ },
|
|
logout: () => { clearTokens(); set({ user: null, token: null }); },
|
|
}));
|
|
```
|
|
|
|
- Один store — одна доменная область (auth, ideas, settings, voice).
|
|
- Store НЕ содержит UI-логику (только состояние + действия).
|
|
- Использовать `useAuthStore()` в компонентах напрямую (без Provider).
|
|
- Context API — только для редких случаев (< 3 подписчиков, Legacy).
|
|
|
|
### Forms (react-hook-form + zod)
|
|
|
|
```ts
|
|
const schema = z.object({
|
|
email: z.string().email(),
|
|
password: z.string().min(8),
|
|
});
|
|
|
|
type FormData = z.infer<typeof schema>;
|
|
|
|
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
|
|
resolver: zodResolver(schema),
|
|
});
|
|
```
|
|
|
|
- Каждая сложная форма (3+ поля) — свой schema-файл в `pages/<name>.schema.ts`.
|
|
- Простые формы (1–2 поля, логин) — schema внутри компонента.
|
|
- Ошибки выводить `<span className="text-red-500 text-sm">`.
|
|
- Кнопку сабмита дизейблить пока `isSubmitting`.
|
|
|
|
### Accessibility (WCAG AA)
|
|
|
|
**Обязательно:**
|
|
- `aria-label` на всех кнопках без текста (иконки: Menu, ThemeToggle, Logout, Close, VoiceChat mic)
|
|
- `htmlFor` + `id` на всех `<label>`/`<input>` парах
|
|
- `role="log"` + `aria-live="polite"` на VoiceChat
|
|
- `<SkipToContent />` — первая ссылка в `<body>`
|
|
- Focus trap в модалках (первый элемент при открытии, возврат при закрытии)
|
|
- Цветовой контраст ≥ 4.5:1 (проверять `text-gray-500` на `bg-gray-800`)
|
|
|
|
**Проверка:** eslint-plugin-jsx-a11y (strict ruleset, errors = fail CI).
|
|
|
|
### Error Boundaries
|
|
|
|
- Глобальный `<ErrorBoundary>` вокруг `<Layout>` в App.tsx.
|
|
- Опционально: per-page ErrorBoundary для VoiceChat, AdminPage (высокая сложность).
|
|
- Fallback: `<ServerErrorPage />` с кнопкой «На главную».
|
|
|
|
```tsx
|
|
<ErrorBoundary fallback={<ServerErrorPage />}>
|
|
<Layout><Outlet /></Layout>
|
|
</ErrorBoundary>
|
|
```
|
|
|
|
### i18n-ready
|
|
|
|
Сейчас — только русский. Но строки НЕ пишутся хардкодом в JSX.
|
|
|
|
```ts
|
|
// constants/strings.ts
|
|
export const strings = {
|
|
nav: { ideas: "Идеи", new: "Новая", voice: "Голос" },
|
|
auth: { login: "Войти", register: "Регистрация" },
|
|
} as const;
|
|
```
|
|
|
|
```tsx
|
|
// Использование
|
|
<T path="auth.login" />
|
|
<T path="nav.ideas" />
|
|
```
|
|
|
|
При переходе на react-i18next — только замена реализации `<T>`, строки не трогать.
|
|
|
|
### ESLint Ruleset (для CI и будущих проектов)
|
|
|
|
**Плагины:**
|
|
- `@eslint/js` — базовые JS правила
|
|
- `typescript-eslint` — TS strict ruleset
|
|
- `eslint-plugin-jsx-a11y` — WCAG AA enforcement
|
|
|
|
**Ключевые правила:**
|
|
```js
|
|
"@typescript-eslint/no-explicit-any": "error",
|
|
"@typescript-eslint/strict-boolean-expressions": "error",
|
|
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
"jsx-a11y/alt-text": "error",
|
|
"jsx-a11y/aria-props": "error",
|
|
"jsx-a11y/aria-role": "error",
|
|
"jsx-a11y/label-has-associated-control": "error",
|
|
"jsx-a11y/click-events-have-key-events": "error",
|
|
"jsx-a11y/no-static-element-interactions": "warn",
|
|
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
"prefer-const": "error",
|
|
"no-var": "error",
|
|
```
|
|
|
|
**Установка в новом проекте:**
|
|
```bash
|
|
npm install -D eslint @eslint/js typescript-eslint eslint-plugin-jsx-a11y
|
|
```
|
|
|
|
### Testing (Vitest)
|
|
|
|
- Имена файлов: `*.test.ts` / `*.test.tsx` рядом с тестируемым модулем.
|
|
- Smoke: каждая страница рендерится без падения.
|
|
- Store: каждый action тестируется (login, logout, create, delete).
|
|
- Формы: заполнить → submit → проверить вызов API.
|
|
- JSON-репортёр для QATesterAgent: `vitest run --reporter=json`.
|
|
- Минимальный coverage: 30% (только критические пути: auth, forms, ErrorBoundary).
|
|
|
|
## React Version Policy
|
|
|
|
- Фиксирована: React 18.3.x LTS
|
|
- Переход на React 19 — только когда:
|
|
1. `vite-plugin-pwa` обновит поддержку
|
|
2. Все зависимости совместимы
|
|
3. Отдельный этап миграции
|
|
|
|
## VPS Compatibility
|
|
|
|
Весь инструментарий (Zustand, react-hook-form, zod, Vitest, ESLint) — npm-пакеты, работают на любой OS. Сборка под VPS:
|
|
```bash
|
|
npm ci # чистая установка под Ubuntu
|
|
npm run build # Vite -> dist/
|
|
```
|