Техническая документация
Архитектура
Приложение построено на Next.js 16 (App Router) с использованием серверных и клиентских компонентов. API реализован через Route Handlers. Тёмная тема реализована через CSS-переменные и @custom-variant dark (Tailwind 4).
app/ ├── api/ # API Route Handlers │ ├── segments/ # CRUD сегментов + overview отчёт │ ├── programs/ # CRUD программ + overview отчёт │ ├── initiatives/ # CRUD инициатив │ ├── tasks/ # CRUD задач │ ├── relations/ # EntityRelation CRUD │ ├── history/ # EntityHistory (GET) │ ├── decision-forks/ # DecisionFork CRUD │ ├── milestones/ # Milestone CRUD (с toggle completed) │ ├── qa/ # QAItem CRUD │ ├── health/ # Расчёт здоровья инициатив (POST) │ ├── stats/ # Агрегированная статистика для дашборда │ ├── settings/ # Настройки (GET/PUT) │ └── excel/ # Импорт/экспорт Excel ├── segments/ # Страницы сегментов ├── programs/ # Страницы программ ├── initiatives/ # Страницы инициатив ├── tasks/ # Страницы задач ├── graph/ # Страница графа ├── roadmap/ # Страница роадмапа ├── settings/ # Настройки (тема, Jira, типы связей) └── docs/ # Документация components/ ├── graph/ # GraphView, CustomNode, CustomEdge, FilterPanel, Legend ├── roadmap/ # RoadmapTimeline ├── layout/ # Sidebar, Header, SidebarContext, LayoutShell ├── providers/ # ThemeProvider └── ui/ # Card, Badge, Modal, RelationsSection, QABlock, etc. lib/ ├── db.ts # Prisma-клиент (PrismaPg adapter) └── health.ts # Логика расчёта здоровья инициатив
Модель данных (Prisma)
ORM: Prisma 7 с адаптером @prisma/adapter-pg.
InvestmentSegment ├── id, name, description, status, owner, label ├── startDate, endDate ├── asIs, limitations, targetState ├── coreDescription, impact (Json), attributes (String[]) ├── jiraKey (String?) └── programs: Program[] Program ├── segmentId → InvestmentSegment ├── jiraKey (String?) └── initiatives: Initiative[] Initiative ├── programId → Program ├── healthStatus, healthIssues (Json), healthUpdatedAt ├── jiraKey (String?) ├── tasks: Task[] └── milestones: Milestone[] Task ├── initiativeId → Initiative └── jiraKey (String?) Milestone ├── description, date, completed (Boolean) └── initiativeId → Initiative (cascade delete) EntityRelation (полиморфная) ├── sourceType, sourceId ├── targetType, targetId ├── type: PART_OF | PREREQUISITE | ENABLES ├── sourceAnchor, targetAnchor (String, default "FINISH"/"START") └── decisionForkId, decisionForkOptionIdx (условные связи) EntityHistory (полиморфная) ├── entityType, entityId ├── field, oldValue, newValue ├── changedAt, changedBy └── entityName (String?) DecisionFork (полиморфная) ├── entityType, entityId ├── question, status: OPEN | RESOLVED | DEFERRED ├── date (DateTime?) └── options (Json) QAItem (полиморфная) ├── entityType, entityId ├── question, answer └── createdAt Settings (key-value) ├── key (unique) └── value (Json)
API Endpoints
| Метод | Путь | Описание |
|---|---|---|
| GET | /api/segments | Список сегментов |
| POST | /api/segments | Создать сегмент |
| GET | /api/segments/[id] | Получить сегмент |
| PUT | /api/segments/[id] | Обновить сегмент |
| DELETE | /api/segments/[id] | Удалить сегмент |
| GET | /api/segments/[id]/overview | HTML-отчёт по сегменту |
| GET/POST | /api/programs | Список / создание программ |
| GET/PUT/DEL | /api/programs/[id] | CRUD программы |
| GET | /api/programs/[id]/overview | HTML-отчёт по программе |
| GET/POST | /api/initiatives | Список / создание инициатив |
| GET/PUT/DEL | /api/initiatives/[id] | CRUD инициативы |
| GET/POST | /api/tasks | Список / создание задач |
| GET/PUT/DEL | /api/tasks/[id] | CRUD задачи |
| GET/POST | /api/relations | Связи (GET с фильтрами entityType/entityId) |
| PUT/DEL | /api/relations/[id] | Обновить/удалить связь |
| GET/POST | /api/milestones | Вехи (фильтр initiativeId) |
| PUT/DEL | /api/milestones/[id] | Обновить (toggle completed) / удалить веху |
| GET | /api/history/[type]/[id] | История изменений сущности |
| GET/POST | /api/decision-forks | Развилки (фильтр entityType/entityId) |
| GET/PUT/DEL | /api/decision-forks/[id] | CRUD развилки |
| GET/POST | /api/qa | Q&A (фильтр entityType/entityId) |
| POST | /api/health | Расчёт здоровья (одной или всех инициатив) |
| GET | /api/stats | Статистика для дашборда |
| GET/PUT | /api/settings | Настройки приложения |
| GET | /api/excel/export | Экспорт в Excel |
| POST | /api/excel/import | Импорт из Excel (multipart) |
Здоровье инициатив
Расчёт здоровья реализован в lib/health.ts. Endpoint: POST /api/health.
// Расчёт одной инициативы
POST /api/health { initiativeId: "..." }
// Расчёт всех инициатив
POST /api/health {}
Результат записывается в поля Initiative:
- healthStatus — GREEN | YELLOW | RED
- healthIssues — JSON с описанием выявленных проблем
- healthUpdatedAt — дата/время расчёта
Запуск и развёртывание
Требования
- Node.js 18+
- Docker (для PostgreSQL)
- npm
Переменные окружения
Команды
docker compose up -d postgres # Запуск БД (порт 5433)
npm install # Установка зависимостей
npm run db:migrate # Миграции Prisma
npm run dev # Dev-сервер (http://localhost:3000)
Ключевые зависимости
@xyflow/react — граф (React Flow v12)dagre — автоматическая раскладка графаrecharts — диаграммы на дашбордеexceljs — генерация и парсинг Exceldate-fns — форматирование дат@prisma/client — ORM (Prisma 7)lucide-react — иконкиclsx / tailwind-merge — утилиты CSS-классовТёмная тема
Тема управляется через ThemeProvider, который переключает класс .dark на document.documentElement.
- CSS-переменные в
globals.css: --background, --foreground, --card-bg, --border, --muted, --accent и др. - Директива
@custom-variant dark (&:where(.dark, .dark *))— необходима для Tailwind 4, чтобыdark:утилиты работали с классом .dark (а не с @media prefers-color-scheme) - Специальные стили для Recharts (fill), ReactFlow (CSS-переменные --xy-*) и скроллбаров