diff --git a/README.md b/README.md index ba7fb963d..96ca2a929 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,15 @@ # Procollab -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.3.5. +Frontend-монорепозиторий для социальной платформы Procollab: Angular 17 приложение (`social_platform`) плюс две разделяемые библиотеки (`core`, `ui`). -## Development server +📖 **Вся документация лежит в [`docs/`](docs/).** Начни с [`docs/PROJECT.md`](docs/PROJECT.md) — там обзор воркспейса (стек, под-проекты, слои, окружения, build/CI), а дальше иди по модулям в [`docs/modules/`](docs/modules/). -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. +## Быстрый старт -## Code scaffolding +```bash +npm ci +npm run start:social # ng serve social_platform --open +``` -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. +Полный список скриптов и команд — в [`docs/PROJECT.md`](docs/PROJECT.md). diff --git a/docs/PROJECT.md b/docs/PROJECT.md new file mode 100644 index 000000000..a413bed56 --- /dev/null +++ b/docs/PROJECT.md @@ -0,0 +1,212 @@ + + +# Procollab — фронтенд-воркспейс + +Angular-монорепозиторий из трёх под-проектов: приложения `social_platform` и двух разделяемых библиотек (`core`, `ui`). Приложение деплоится в две среды: prod (с ветки `master`) и dev-stage (с ветки `dev`). + +> Документация по конкретным модулям лежит в [`docs/modules/`](modules/), документация по слоям приложения — в [`docs/social-platform/`](social-platform/), cross-cutting API-сервисы описаны в [`docs/cross-cutting.md`](cross-cutting.md), документация по библиотеке `core` — в [`docs/core/`](core/), по библиотеке `ui` — в [`docs/uilib.md`](uilib.md). Этот файл — общая карта воркспейса. + +--- + +## Стек + +| Область | Технология | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Framework | Angular 17 (standalone components, signals, control-flow `@if/@for`) | +| Язык | TypeScript 5.2 (`strict`, `noImplicitOverride`, `noPropertyAccessFromIndexSignature`, `strictTemplates`, `useDefineForClassFields: false`) | +| Состояние | Signals + собственный дискриминатор `AsyncState` (`initial` / `loading` / `success` / `failure`) | +| Async | RxJS 7.5 | +| HTTP | `ApiService` поверх `HttpClient` + `BearerTokenInterceptor` + `CamelCaseInterceptor` + `LoggingInterceptor` | +| Auth | JWT в cookies через `TokenService`; имена cookie-ключей переключаются между `accessToken`/`refreshToken` и `devAccessToken`/`devRefreshToken` в зависимости от hostname | +| WebSocket | `WebsocketService` с reconnect interval / max attempts из `environment` | +| UI primitives | `@angular/cdk` 16, `@angular/material` 16 (выборочные модули) | +| Стили | SCSS + миксины из `styles/_responsive.scss` (`apply-desktop` ≥ 1000px, `apply-tablet` 750–999px, `apply-tablet-and-above` ≥ 750px) | +| Errors / observability | `LoggerService` (уровень + timestamp), `GlobalErrorHandler`, `LoggingInterceptor`, `EventBus` для domain-событий; `environment.sentryDns` есть, но Sentry SDK в текущем коде не интегрирован | +| Сборка | Angular CLI 17, ng-packagr 17 для библиотек | +| Тесты | Karma + Jasmine 4 (`karma.conf.js`) | +| Lint / format | ESLint, Stylelint, Prettier; `precommit` запускает `lint:scss` и `lint:ts` | +| SVG-спрайт | `svg-sprite` собирает `assets/icons/symbol/svg/sprite.css.svg` из всех файлов в `assets/icons/svg/**/*.svg` | +| Сторонние пакеты | `dayjs`, `class-transformer`, `js-cookie`, `linkifyjs`, `nanoid`, `ng-click-outside`, `ngx-mask`, `ngx-image-cropper`, `ngx-autosize`, `file-saver`, `fuse.js`, `js-base64`, `camelcase-keys`, `snakecase-keys` | +| Node | `~18.16` (CI ставит `18.13` / `18.x`) | + +--- + +## Под-проекты (`angular.json` → `projects`) + +| Проект | Тип | Source root | Назначение | +| ----------------- | ----------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `social_platform` | application | `projects/social_platform/src` | Веб-приложение для пользователей. Содержит всё, что лежит в `app/`: `domain`, `api`, `infrastructure`, `ui`, `utils`, плюс ассеты в `assets/` и шрифты. Это единственный application-проект; library-проекты собираются отдельно через `ng-packagr`. | +| `core` | library | `projects/core/src` | Базовые сервисы и утилиты, которые в принципе могут переехать в любое Angular-приложение: `ApiService`, `TokenService`, HTTP-интерсепторы, пайпы, `LoggerService`, `GlobalErrorHandler`, `ValidationService`, `WebsocketService`, провайдеры (`PRODUCTION`, `API_URL`), guards (`auth-required`, `projects-edit`, `profile-edit`). Публичный API — `core/src/public-api.ts`, импортируется как `@corelib`. Константы (списки, навигация) лежат отдельно в `core/src/consts/`. | +| `ui` | library | `projects/ui/src` | Layout-компоненты, которые шарятся между приложениями (sidebar, header, profile-info, profile-control-panel, invite-manage-card, subscription-plans), плюс маленький набор примитивов (`avatar`, `back`, `icon`). Публичный API — `ui/src/public-api.ts`, импортируется как `@uilib`; модели — через `uilib/models`. | + +--- + +## TypeScript path aliases (`tsconfig.json` → `compilerOptions.paths`) + +| Alias | Указывает на | +| ------------------- | ---------------------------------------------------------- | +| `@core/*` | `projects/core/src/*` | +| `@corelib` | `projects/core/src/public-api.ts` | +| `@uilib` | `projects/ui/src/public-api.ts` | +| `uilib/models` | `projects/ui/src/models/*` | +| `@domain/*` | `projects/social_platform/src/app/domain/*` | +| `@infrastructure/*` | `projects/social_platform/src/app/infrastructure/*` | +| `@api/*` | `projects/social_platform/src/app/api/*` | +| `@ui/*` | `projects/social_platform/src/app/ui/*` | +| `@pages/*` | `projects/social_platform/src/app/ui/pages/*` | +| `@utils/*` | `projects/social_platform/src/app/utils/*` | +| `@environment` | `projects/social_platform/src/environments/environment.ts` | +| `core` / `ui` | `dist/core` / `dist/ui` (сборочные артефакты библиотек) | + +--- + +## Слои приложения (`projects/social_platform/src/app/`) + +``` +domain/ чистые типы, модели, repository ports, domain events +api/ use-cases (одна операция = один класс) + facades + UI-info services +infrastructure/ реализации репозиториев (HTTP), DTO ↔ domain adapters, DI providers +ui/ routes, pages, widgets, primitives, ui-services (loading/snackbar/nav/notification) +utils/ маленькие чистые хелперы (валидаторы, форматтеры, file-export и т. п.) +``` + +Правила зависимостей: + +``` +ui ─┬──▶ api ──▶ domain ◀── infrastructure + └────────────▶ domain (только для типов) +``` + +Никто не импортирует `infrastructure` напрямую — он подключается DI-провайдерами (`infrastructure/di/.providers.ts`), которые биндят порт к реализации. + +### Подробнее по слоям + +- **`domain/`** — каркас. На каждый домен (auth, project, vacancy, courses, …) есть папка с: + + - `*.model.ts` — TypeScript-интерфейсы и классы доменной модели; + - `ports/*.repository.port.ts` — абстрактные классы (используются как DI-токены) с контрактом репозитория; + - `events/*` — domain events (если используется EventBus); + - `commands/`, `results/` — отдельные структуры команд/результатов use-case'ов в новых модулях. + +- **`api/`** — оркестрация. На каждый домен: + + - `use-cases/..use-case.ts` — один класс на одну операцию, инжектит `*RepositoryPort`, возвращает `Observable` или `Observable>`; + - `facades/*.service.ts` — фасады для UI: хранят `signal>`, вызывают use-case'ы, обрабатывают результат; + - `facades/ui/*.service.ts` — UI-info-сервисы: composed `computed` сигналы поверх фасадов (готовые boolean'ы для `*ngIf`, отфильтрованные списки, форматированные тексты и т. п.). + +- **`infrastructure/`**: + + - `repository//*` — реализации портов, делают HTTP через `ApiService`, держат `EntityCache` где нужно; + - `adapters/*` — трансформации DTO ↔ domain; + - `di/.providers.ts` — `Provider[]` массив, который биндит каждый порт к его реализации (`{ provide: XRepositoryPort, useExisting: XRepository }`). + +- **`ui/`**: + - `routes//*.routes.ts` — лениво подгружаемые группы роутов; + - `pages//...` — страницы (smart-компоненты), потребляют фасады; + - `widgets/*` — переиспользуемые блоки внутри `social_platform` (виджет ≈ используется в одном-двух местах); + - `primitives/*` — атомы (input, button, modal, dropdown, …) — используются повсеместно; + - `services/` — runtime-сервисы UI: `LoadingService`, `SnackbarService`, `NavService`, `NotificationService`. + +### Cross-cutting блоки + +| Блок | Где | Что | +| -------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `AsyncState` | `domain/shared/async-state.ts` | Дискриминатор `{ status: "initial" \| "loading" \| "success" \| "failure" }` с хелперами `initial`, `loading`, `success`, `failure`, `isSuccess`, `isLoading`, `isFailure`. Единый источник правды для состояния асинхронных операций в фасадах. | +| `Result` | `domain/shared/result.type.ts` | `ok(data)` / `fail(error)` для возврата из use-case'ов. Use-case никогда не бросает — всё через `Result`. | +| `EventBus` + domain events | `domain//events/*` + `infrastructure` listeners | Репозитории слушают доменные события и инвалидируют свой `EntityCache`. Развязывает модули между собой. | +| `EntityCache` | `infrastructure/repository/...` | Простой in-memory cache без TTL. Project и Vacancy инвалидируются через `EventBus` и ручные вызовы `invalidate()`, Courses инвалидирует detail/structure cache после отправки ответа, Program кеширует `getOne()` без текущих listeners. | +| `LoggingInterceptor` + `LoggerService` | `core/lib/interceptors`, `core/lib/services/logger` | Структурированные логи с timestamp и уровнем; DEBUG только не в проде. | +| `GlobalErrorHandler` | `core/lib/services/error` | Перехватывает необработанные ошибки и Promise rejections, логирует через `LoggerService.error("[GlobalError] ...")`. `ErrorService` и `NgZone` заинжекчены, но в текущем коде не используются. | + +--- + +## Окружения + +`fileReplacements` в `angular.json` подменяет `environment.ts` → `environment.prod.ts` для конфигурации `production`. + +| Ключ | dev (`environment.ts`) | prod (`environment.prod.ts`) | +| ---------------------------------- | --------------------------- | ---------------------------- | +| `production` | `false` | `true` | +| `apiUrl` | `https://dev.procollab.ru` | `https://api.procollab.ru` | +| `websocketUrl` | `wss://dev.procollab.ru/ws` | `wss://api.procollab.ru/ws` | +| `websocketReconnectionInterval` | `500` мс | `5000` мс | +| `websocketReconnectionMaxAttempts` | `5` | `5` | +| `sentryDns` | общий DSN | общий DSN | + +### Изоляция cookies между prod и dev-stage + +`TokenService` (`projects/core/src/lib/services/tokens/token.service.ts`) выбирает имена cookie-ключей по hostname: + +| Hostname | Access cookie | Refresh cookie | +| ------------------ | ---------------- | ----------------- | +| `dev.procollab.ru` | `devAccessToken` | `devRefreshToken` | +| Любой другой | `accessToken` | `refreshToken` | + +Это позволяет одному разработчику быть залогиненным одновременно в prod и dev-stage в одном браузере без коллизий по cookie-куки. Прод-cookies дополнительно имеют `domain: ".procollab.ru"`, `secure: true`, `sameSite: "None"` и `expires` через 30 дней (см. `getCookieOptions()`); в dev cookies остаются на текущем хосте с дефолтами браузера. + +--- + +## Сборка, запуск, тесты + +```bash +npm run start:social # ng serve social_platform --open +npm run build:social:dev # build:sprite + ng build social_platform --configuration=development +npm run build:social:prod # build:sprite + ng build social_platform --configuration=production +npm run build:pr # alias для build:social:dev (используется CI для dev-stage) +npm run build:prod # alias для build:social:prod (используется CI для prod) +npm run watch # ng build --watch development + +npm run test # ng test (Karma + Jasmine, headed Chrome) +npm run test:ci # ng test --browsers=Headless --no-watch + +npm run lint:ts # ESLint поверх projects/**/*.ts +npm run lint:scss # Stylelint поверх projects/**/*.scss --fix +npm run format # Prettier write +npm run format:check # Prettier check + +npm run build:sprite # SVG-спрайт из src/assets/icons/svg/**/*.svg +npm run docs:json # Compodoc JSON dump (legacy) +``` + +`precommit` (через пакет `pre-commit`): `lint:scss`, `lint:ts`. Hooks **не пропускаем** через `--no-verify` — если линтер падает, чиним причину. + +`build:sprite` обязательный шаг перед сборкой: он собирает `assets/icons/symbol/svg/sprite.css.svg`, на который завязан `IconComponent` (через ``). + +--- + +## CI/CD + +Два workflow в `.github/workflows/`: + +| Файл | Триггер | Что делает | +| ---------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `deploy.yml` | `push` в `master` (или ручной `workflow_dispatch`) | `npm ci` → `format:check` → `lint:ts` → `build:prod` → `rsync` дистрибутива в `app.procollab.ru:/home/gh_deploy` (секрет `DEPLOY_KEY`). | +| `deploy-dev.yml` | `push` в `dev` | `npm ci` → `format:check` → `lint:ts` → `build:pr` → `rsync` дистрибутива в `dev.procollab.ru:/home/front/app` (секрет `DEPLOY_KEY_FRONT_DEV`). | + +Отдельного PR-preview workflow нет — раньше был `pull_request.yml` с Netlify-превью, на ветке `dev` он удалён. + +Если ломается `format:check` или `lint:ts` — деплой не пойдёт. Перед пушем в `dev` или PR в `master` локально гонять: + +```bash +npm run format:check && npm run lint:ts && npm run build:pr +``` + +--- + +## Точки входа в роутинг + +`projects/social_platform/src/app/app.routes.ts` — корневой `Routes` массив. Лениво подгружаемые группы лежат под `ui/routes//`: + +- **`auth`** — публичные экраны (`ui/routes/auth/`): login, register, email verification, password reset. +- **`office`** — закрытый shell после логина (`ui/routes/office/`). Дочерние роуты: + - `feed` — глобальная лента; + - `members` — список участников платформы; + - `vacancies` — список вакансий, детальная страница; + - `chats` — список чатов и детальные чаты (личные + проектные); + - `projects` — `dashboard`/`my`/`subscriptions`/`invites`/`all` списки + edit + детальная страница с детьми: `info`, `vacancies`, `team`, `work-section`, `chat`; + - `program` — `all`, детальная страница (`main`, `projects`, `members`, `projects-rating`) и `register`; + - `courses` — `all`, детальная страница с детьми: `info`, `lesson` (и дальнейшие lesson-children); + - `onboarding` — flow первого захода (привязан к office shell, но грузится по своему пути). +- **`error`** — `404` и общая страница ошибки. + +--- diff --git a/docs/core/consts.md b/docs/core/consts.md new file mode 100644 index 000000000..7de1c94d7 --- /dev/null +++ b/docs/core/consts.md @@ -0,0 +1,111 @@ + + +# `@corelib` — consts + +Все константы лежат в `projects/core/src/consts/` (вне `lib/`, отдельная иерархия). Не реэкспортируются из `core/src/public-api.ts` — потребители импортируют их через `@core/consts/...` (alias `@core/*` указывает на `projects/core/src/*`). + +## Структура папок + +``` +core/src/consts/ + filters/ # массивы для фильтров (feed, vacancies, ratings) — value/label/id + lists/ # справочники для select-полей (educations, languages, etc.) + navigation/ # элементы боковой/верхней навигации + other/ # всё, что не попало в другие группы (цвета, иконки, profile-fields, kanban-* — см. ниже) +``` + +--- + +## Правила нейминга + +(Сохранено из удалённого `projects/core/src/consts/README.md`.) + +### Имена файлов + +- Формат: `feature.const.ts` +- Стиль: **kebab-case** +- Примеры: `navigation.const.ts`, `selects.const.ts`, `permissions.const.ts` + +### Имена переменных + +- Стиль: **camelCase** +- Если переменная — список, имя во **множественном числе** +- Имя отражает назначение +- Экспорт только через `export const` + +```ts +export const navItems = [...]; +``` + +--- + +## `consts/lists/` — справочники для `` + +Формат каждой константы: `{ id: number, value: string, label: string }[]` (иногда + дополнительные поля). + +| Файл | Экспорт | Где используется | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| `actiion-type-list.const.ts` | `actionTypeList` (3 элемента: action/call/meet) | Канбан task-detail (выбор типа задачи). | +| `direction-project-list.const.ts` | `directionProjectList` (8 направлений: Технология, IT, Транспорт, Хим Био, Дизайн, Мультимедиа, СоцТех, Урбанистика) | Создание/редактирование проекта. | +| `education-info-list.const.ts` | `educationUserType` (3) + `educationUserLevel` (5) | Profile edit — образование. | +| `language-info-list.const.ts` | `languageNamesList` | Profile edit — языки. | +| `mock-months-list.const.ts` | `mockMonthsList` | Месяцы для дат рождения и т. п. | +| `priority-info-list.const.ts` | `priorityInfoList` (6 приоритетов: бэклог/в ближайшие часы/высокий/средний/низкий/улучшение, с цветом и `priorityType` для бэка) | Канбан task-detail (выбор приоритета). | +| `resource-options-list.const.ts` | `resourceOptionsList` | Project edit — типы ресурсов. | +| `roles-members-list.const.ts` | `rolesMembersList` | Members filters / project team. | +| `track-project-list.const.ts` | `trackProjectList` | Программные треки. | +| `work-experience-list.const.ts` | `workExperienceList` | Vacancy / profile (опыт работы). | +| `work-format-list.const.ts` | `workFormatList` | Vacancy (формат работы — онлайн/гибрид/офис). | +| `work-schelude-list.const.ts` | `workScheludeList` (опечатка) | Vacancy (график). | + +--- + +## `consts/filters/` — конфигурации фильтров + +| Файл | Экспорт | +| --------------------------------- | ---------------------- | +| `feed-filter.const.ts` | `feedFilter` | +| `rating-filter.const.ts` | `ratingFilters` | +| `tags-filter.const.ts` | `tagsFilter` | +| `work-experience-filter.const.ts` | `workExperienceFilter` | +| `work-format-filter.const.ts` | `workFormatFilter` | +| `work-schedule-filter.const.ts` | `workScheduleFilter` | + +Используются виджетами `widgets/feed-filter`, `widgets/projects-filter`, `widgets/vacancy-filter`. Структура — массив групп с фильтрами. + +--- + +## `consts/navigation/` — элементы пошагового редактирования + +| Файл | Экспорт | Что | +| ---------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `nav-profile-items.const.ts` | `navProfileItems` | Шаги редактирования профиля (`main` / `education` / `experience` / `achievements` / `additional`), каждый шаг → `{ step: EditStep, src: iconName, label: string }`. | +| `nav-project-items.const.ts` | `navProjectItems` | Шаги редактирования проекта (`main` / `contacts` / `achievements` / `vacancies` / `team` / `additional`). | + +Тип `EditStep` импортируется из `projects/social_platform/src/app/api/project/project-step.service` — это ещё одна зависимость `core` → `social_platform`. + +--- + +## `consts/other/` + +| Файл | Экспорт | Что | Статус | +| ----------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| `profile-fields.const.ts` | `profileFields` | Конфигурация полей профиля: `{ key, type: "array" \| "string" }[]`. Используется при сериализации формы edit. | актив | +| `quick-answers.const.ts` | `QuickAnswers` (PascalCase) | 5 готовых причин отказа от выполнения канбан-задачи. | **используется только канбаном** — модуль отключён | +| `tag-colors.const.ts` | `tagColors` | 10+ цветов для UI-тегов: `{ id, name: string, color: hex }`. | актив (используется в ``) | +| `trajectory-more.const.ts` | `trajectoryMore` | Items для меню "ещё" в траектории курсов. | актив | +| `kanban-column-info.const.ts` | `kanbanColumnInfo` | Опции dropdown для колонки канбана. | **kanban-only**, модуль отключён | +| `kanban-icons.const.ts` | `KanbanIcons` (PascalCase) | Иконки досок канбана. | **kanban-only**, модуль отключён | + +--- + +## Как добавлять новую константу + +1. Создать `.const.ts` в подходящей папке (`lists/` для справочников `