add rules

This commit is contained in:
2026-10-03 13:45:34 +07:00
parent 4ee5fb998e
commit 0248a77f61
2 changed files with 179 additions and 100 deletions
+168
View File
@@ -0,0 +1,168 @@
# Стандарт написания фронтенда
Статус: обязательный стандарт.
Этот документ — единственный источник правил для нового и изменяемого кода в
`evening_detective_frontend`. Существующий код не переписывается только ради
соответствия стандарту: при изменении файла применяй правила к затронутому коду
и не расширяй diff без необходимости.
## 1. Базовые решения
- Стек: Vue 3, `<script setup lang="ts">`, TypeScript, Vite, Pinia, Vue Router,
Naive UI и `@vicons`.
- UI-тексты пишутся на русском.
- Новые зависимости, альтернативные UI-библиотеки и новый state manager не
добавляются без явного разрешения пользователя.
- Сначала переиспользуй существующий компонент, composable, util, store или
токен. Новая абстракция нужна только при повторном использовании или когда
без неё нарушается граница ответственности.
- Не добавляй код «на будущее», универсальные обёртки с одним потребителем и
дублирующие типы.
## 2. Куда класть новый код
| Что добавляется | Единственное основное место | Правило |
|---|---|---|
| Маршрутная страница | `src/views/*View.vue` | Только подключает страницу из `components` и не содержит бизнес-логики. |
| Экран или крупный блок страницы | `src/components/*Page.vue` | Собирает экран, управляет его flow и передаёт данные дочерним компонентам. |
| Переиспользуемый визуальный блок | `src/components/*.vue` | Не знает о конкретном маршруте; получает данные через props и сообщает событиями. |
| Логика нескольких компонентов | `src/composables/use*.ts` | Состояние и lifecycle-композиция Vue; один composable — одна задача. |
| Чистая функция без Vue-состояния | `src/utils/*.ts` | Форматирование, преобразование и вычисления; без компонентов и store. |
| Общее состояние | `src/stores/*.ts` | Только данные и действия с общим жизненным циклом. |
| Тип доменной модели или редактора | `src/types/*.ts` | Только типы приложения, которых нет в generated API. |
| API-вызов | `src/api/*.ts` или store/page flow | Через сгенерированный клиент и существующий транспорт. |
| Сгенерированный API | `src/api/generated/**` | Только результат `npm run proto:gen`; ручные правки запрещены. |
| Глобальный CSS и токен | `src/assets/base.css` / `src/assets/main.css` | Только при действительно глобальном эффекте. |
| Стиль компонента | `<style scoped>` компонента | Одноразовый стиль не выносить в глобальный CSS. |
Если код подходит под несколько строк, выбирай первую подходящую границу сверху.
Нельзя прятать бизнес-логику в `utils`, API-вызовы в визуальном компоненте или
локальное состояние в глобальном store.
## 3. Компоненты Vue
Каждый компонент имеет одну ответственность и читается сверху вниз: импорты;
`props`, `emits` и локальные типы; state и `computed`; обработчики и
асинхронные действия; template; scoped style.
- Используй `<script setup lang="ts">`.
- Props и emits всегда типизируй. Для текущего проекта допустимы typed
`defineProps` и `Object as PropType<T>`.
- Производные значения оформляй через `computed`, не дублируй их в `ref`.
- Локальное состояние держи в компоненте; родитель передаёт данные через props,
ребёнок сообщает намерение через emits.
- Не передавай весь store или необработанный API-ответ компоненту, которому
нужен только небольшой набор полей.
- Не делай компонент одновременно страницей, API-клиентом и универсальным
контейнером.
- Повторяемую логику выноси в composable только после появления реальной общей
границы.
Маршрутный flow:
```text
маршрут -> src/views/CatalogView.vue -> src/components/CatalogPage.vue
```
`*View.vue` только подключает page-компонент. Page-компонент собирает экран,
загружает его данные и управляет flow.
## 4. TypeScript и именование
- `PascalCase.vue` — компоненты; `camelCase.ts` — TypeScript; composable —
`useName.ts`; store — `camelCase.ts`.
- Компоненты, типы и интерфейсы — `PascalCase`; функции, переменные и props —
`camelCase`; boolean — с префиксом `is`, `has`, `can` или `should`.
- Типы protobuf импортируй из `src/api/generated/**` и не дублируй в `src/types`.
- Типы форм и редакторов храни в `src/types` либо рядом с компонентом, если они
не используются за его пределами.
- Не используй `any`; неизвестное внешнее значение должно быть `unknown` с
последующим сужением типа.
- Не подавляй TypeScript через `as` без причины и не меняй API-контракт молча.
- Импорты: внешние пакеты, затем `@/`, затем относительные; неиспользуемые
импорты запрещены.
- Без точек с запятой, одинарные кавычки, Prettier с шириной 100.
## 5. API, асинхронность и ошибки
- API вызывается только через generated clients и существующие обёртки
(`getAuthClient()` и аналоги). Не создавай ручные fetch/axios-вызовы для уже
описанного endpoint.
- Ошибку обрабатывай в том же flow. Пользовательские сообщения показывай через
`useMessage()` из Naive UI и пиши по-русски.
- Загрузку держи в простом `ref(false)` с `try/finally`; повторную отправку
блокируй, если она может испортить данные.
- Поллинг делай через существующий `useSimplePolling`.
- Ошибка не должна теряться в `catch`: покажи её или передай выше с контекстом.
`console.error` оставляй для диагностики после пользовательской обработки.
- Пустое, загрузочное и ошибочное состояния должны быть явными в template.
- Не перезаписывай локальный черновик данными фонового обновления во время
редактирования.
## 6. State и stores
- В store держи только состояние, которым пользуются разные маршруты или
компоненты, и действия, изменяющие это состояние.
- Не превращай store в контейнер всех запросов приложения.
- Преобразуй API-ответ в форму для UI на границе store/page flow.
- Секреты и токены не выводи в UI и не помещай в URL.
- Локальный черновик держи в `ref`/`reactive`; серверное состояние
синхронизируй явно после сохранения.
## 7. Стили и дизайн
- Компонентные стили по умолчанию пишутся в `<style scoped>`.
- CSS-свойства располагаются в алфавитном порядке; это проверяет Stylelint.
- Новые цвета не хардкодь: используй палитру и CSS-переменные из
`src/assets/base.css`. Новый глобальный токен добавляй в `:root` только при
необходимости и с понятным семантическим именем.
- Для экранного UI используй компоненты Naive UI (`n-*`) и тему из `App.vue`, а
не стилизацию нативных `<input>` и `<button>`.
- Иконки подключай из `@vicons` через `Icon` из `@vicons/utils`; размер задавай
через `size`.
- Адаптивность проектируй mobile-first; desktop-брейкпоинт —
`@media (width >= 1024px)`.
- Сохраняй текущий flow: `HeaderMenu` сверху, `.center-block-custom` для
прокрутки, `.width700` для дел и `.width1200` для списков.
- Бумажные листы используют `MessagePaper` и `font_old_typer`; UI-формы, меню и
кнопки этот шрифт не используют.
- Не добавляй глобальный utility-класс для одного компонента.
## 8. Формы, доступность и состояния
- Валидируй ввод до API-вызова и учитывай серверную ошибку; клиентская проверка
не заменяет серверную.
- У обязательного поля есть понятная подпись и сообщение об ошибке.
- Во время загрузки показывай состояние и не допускай повторной отправки.
- Интерактивные элементы доступны с клавиатуры и имеют видимый focus. Не убирай
outline без равноценной замены.
- Модальные окна просмотра делай через `n-modal` + `n-card` с `role="dialog"`
и `aria-modal="true"`; изображения имеют осмысленный `alt`.
- Hover не единственный способ понять состояние: учитывай focus, disabled,
loading, success и error.
## 9. Тесты и проверки
- Для чистой функции с нетривиальной логикой добавь небольшой тест рядом с ней
или используй существующий механизм тестирования.
- Не добавляй тесты для тривиального прокси или очевидной разметки; проверяй
пользовательский flow, валидацию, преобразования и ошибки.
- Перед передачей фронтенд-задачи запускай:
```bash
npm run type-check
npm run lint
npm run lint:css
```
- После изменений сборки, маршрутов или generated API дополнительно запускай
`npm run build`; после изменения `proto/main.proto` сначала запускай
`npm run proto:gen`.
## 10. Допустимое отклонение
Отклонение от стандарта требует до изменения кода: явного разрешения
пользователя, короткого письменного обоснования и указания затронутых файлов.
Если существующий код противоречит стандарту, не исправляй его попутно:
исправь только необходимый участок и отдельно предложи миграцию.