Техническая документация

Архитектура

Приложение построено на 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]/overviewHTML-отчёт по сегменту
GET/POST/api/programsСписок / создание программ
GET/PUT/DEL/api/programs/[id]CRUD программы
GET/api/programs/[id]/overviewHTML-отчёт по программе
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/qaQ&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

Переменные окружения

DATABASE_URL=postgresql://user:password@localhost:5433/itstrategy

Команды

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 — генерация и парсинг Excel
date-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-*) и скроллбаров