pcp: создание плана новой цепочки из вкладки «Планы» (база/без истории, окно из двух ЗРВ)

Оператор берёт за основу прошлый план (предыстория) или начинает цепочку с нуля,
набирает окно из двух реальных ЗРВ и создаёт план-кандидат; сразу показывается прогноз
будущей цепочки от него. Рабочим (якорем) план становится после ручной отправки (promote)
и приёмки — отдельного механизма якорения не вводим.

pcp-tgu-service:
- сырые ЗРВ: PlanCalculationService.rawWindows + PlanProjectionService.rawZrvWindows,
  GET /api/plans/{spacecraftId}/zrv (исходные окна снапшота, не cooked-проекция);
- CreateChainPlanUseCase + POST /api/plans/new-chain: опциональная база (клон включений
  в окно) либо пустая миссия без истории; прогноз цепочки через projectAfterWindow;
- ZrvWindowMatcher (старт+КПП одной зоны, конец — другой); assertDraftSlotStillValid
  переведён на него — единая проверка для replace/fork-forward и окна из двух ЗРВ;
- тесты: CreateChainPlanUseCaseTest (8), ZrvWindowMatcherTest (7), правки promote-тестов.

pcp-mission-planing-service:
- POST /api/missions/blank — пустая миссия для сценария «без истории» (+ 2 теста).

pcp-tgu-ops-ui:
- API fetchZrvWindows/createChainPlan; диалог «Создать план» во вкладке «Планы»
  (выбор базы, стартовой и конечной ЗРВ) с показом спрогнозированной цепочки и ручной
  отправкой; подсветка ЗРВ выбранного КА на таймлайне;
- «Целевая ЗРВ» убрана из редактора (он остаётся под replace-правку); удалён ставший
  мёртвым editorForkForward; createChainWindows + тесты.
This commit is contained in:
Дмитрий Соловьев
2026-06-03 16:25:46 +03:00
parent 057452f19d
commit 9c2d8516bc
26 changed files with 1316 additions and 240 deletions
@@ -1358,3 +1358,87 @@
grid-template-columns: 1fr;
}
}
/* «Создать план»: кнопка над таймлайном + модальный диалог новой цепочки. */
.tgu-center__actions {
display: flex;
justify-content: flex-end;
padding: 0.5rem 0.75rem 0;
}
.tgu-chain-dialog__backdrop {
position: fixed;
inset: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(2px);
}
.tgu-chain-dialog {
width: min(34rem, calc(100vw - 2rem));
max-height: calc(100vh - 3rem);
overflow: auto;
display: flex;
flex-direction: column;
gap: 0.8rem;
padding: 1rem 1.1rem 1.1rem;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--bg-panel);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}
.tgu-chain-dialog__header {
display: flex;
align-items: center;
justify-content: space-between;
}
.tgu-chain-dialog__header h2 {
margin: 0;
font-size: 1.05rem;
}
.tgu-chain-dialog__field {
display: flex;
flex-direction: column;
gap: 0.3rem;
font-size: 0.82rem;
}
.tgu-chain-dialog__field > span {
color: var(--text-muted, #9fb0b3);
}
.tgu-chain-dialog__actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 0.3rem;
}
.tgu-chain-dialog__chain {
margin: 0;
padding-left: 1.1rem;
font-size: 0.8rem;
line-height: 1.5;
max-height: 12rem;
overflow: auto;
}
.tgu-chain-dialog__hint {
font-size: 0.78rem;
color: var(--text-muted, #9fb0b3);
}
/* ЗРВ выбранного КА на таймлайне — лёгкая подсветка доступных зон. */
.tgu-timeline__zrv {
fill: rgba(90, 170, 200, 0.16);
stroke: rgba(90, 170, 200, 0.5);
stroke-width: 1;
rx: 2;
pointer-events: all;
}