Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# 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/
|
||||
```
|
||||
Reference in New Issue
Block a user