Files
dc-observatio/services/pcp-tgu-ops-ui/src/features/tgu-editor/EditorToolbar.tsx
T
Дмитрий Соловьев 9c2d8516bc 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 + тесты.
2026-06-03 16:25:46 +03:00

131 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { TguPlanUi, TguPlatformUi } from "../../model/timelineTypes";
import { formatDuration } from "./editorPresentation";
type EditorToolbarProps = {
selectedSpacecraftId?: string;
selectedPlan?: TguPlanUi;
platforms: TguPlatformUi[];
canUndo: boolean;
canRedo: boolean;
checking: boolean;
conflictCount: number;
addedCount: number;
dirty: boolean;
applying: boolean;
canceling: boolean;
windowLabel: string;
rightMode: "none" | "inspect" | "shoot" | "downlink" | "mapInfo";
onSetRightMode: (mode: "shoot" | "downlink") => void;
onUndo: () => void;
onRedo: () => void;
onCheck: () => void;
onApply: () => void;
onCancel: () => void;
onZoom: (factor: number) => void;
onPan: (factor: number) => void;
};
export function EditorToolbar({
selectedSpacecraftId,
selectedPlan,
platforms,
canUndo,
canRedo,
checking,
conflictCount,
addedCount,
dirty,
applying,
canceling,
windowLabel,
rightMode,
onSetRightMode,
onUndo,
onRedo,
onCheck,
onApply,
onCancel,
onZoom,
onPan
}: EditorToolbarProps) {
const platformLabel = selectedSpacecraftId
? platforms.find((platform) => platform.spacecraftId === selectedSpacecraftId)?.name ?? selectedSpacecraftId
: "КА не выбран";
return (
<div className="tgu-editor-toolbar">
<div className="tgu-editor-toolbar__identity">
<span className="tgu-editor-type-dot" aria-hidden="true" />
<div>
<div className="tgu-editor-toolbar__title">{platformLabel}</div>
<div className="tgu-editor-toolbar__meta">
{selectedPlan ? `${selectedPlan.planId} · ${selectedPlan.status}` : "Выберите план на timeline"}
</div>
</div>
</div>
<div className="tgu-editor-toolbar__group">
<button
className={`tgu-button ${rightMode === "shoot" ? "tgu-button--primary" : ""}`}
type="button"
onClick={() => onSetRightMode("shoot")}
disabled={!selectedSpacecraftId}
>
+ Съёмка
</button>
<button
className={`tgu-button ${rightMode === "downlink" ? "tgu-button--primary" : ""}`}
type="button"
onClick={() => onSetRightMode("downlink")}
disabled={!selectedSpacecraftId}
>
+ Сброс
</button>
</div>
<div className="tgu-editor-toolbar__spacer" />
<div className="tgu-editor-window-control" aria-label="Окно редактора">
<button type="button" onClick={() => onPan(-0.25)} title="Назад по времени"></button>
<button type="button" onClick={() => onZoom(1.4)} title="Отдалить"></button>
<span>{windowLabel || formatDuration(0)}</span>
<button type="button" onClick={() => onZoom(0.7)} title="Приблизить">+</button>
<button type="button" onClick={() => onPan(0.25)} title="Вперёд по времени"></button>
</div>
<div className="tgu-editor-toolbar__group">
<button className="tgu-icon-button" type="button" onClick={onUndo} disabled={!canUndo} title="Отменить">
</button>
<button className="tgu-icon-button" type="button" onClick={onRedo} disabled={!canRedo} title="Вернуть">
</button>
</div>
<button
className={`tgu-button ${conflictCount > 0 ? "tgu-editor-button--warning" : ""}`}
type="button"
onClick={onCheck}
disabled={checking || !selectedSpacecraftId}
>
{checking ? "Проверка..." : conflictCount > 0 ? `Конфликты · ${conflictCount}` : "Проверить"}
</button>
{dirty && (
<button
className="tgu-button tgu-button--reject"
type="button"
onClick={onCancel}
disabled={canceling || applying}
>
{canceling ? "Отмена..." : "Отмена"}
</button>
)}
<button className="tgu-button tgu-button--primary" type="button" onClick={onApply} disabled={!dirty || applying || canceling}>
{applying ? "Применение..." : `Применить${addedCount > 0 ? ` · +${addedCount}` : ""}`}
</button>
</div>
);
}