feat(tgu-ops-ui): add spacecraft work editor tab

Port the TGU editor prototype into pcp-tgu-ops-ui as a new editor tab, add draft/conflict/pass logic with tests, extend the 2D map picking API, and include editor styles/theme aliases and task/prototype docs.

Validation: npm run test; npm run build in services/pcp-tgu-ops-ui.
This commit is contained in:
Дмитрий Соловьев
2026-05-30 15:28:34 +03:00
parent c3a1a8b4a1
commit f084775cbb
58 changed files with 6609 additions and 7 deletions
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { fetchPlans, fetchPlatforms, sendPlanDecision } from "../../api/tguApi";
import type { TguPlanDecision, TguPlatform } from "../../model/tguTypes";
import type { TguPlanUi, TimelineRange } from "../../model/timelineTypes";
import type { TguActiveTab, TguPlanUi, TimelineRange } from "../../model/timelineTypes";
import { TguEditorTab } from "../tgu-editor/TguEditorTab";
import { Tgu2DMapTab } from "../tgu-map-2d/Tgu2DMapTab";
import { TguPlanDetails } from "./TguPlanDetails";
import { TguPlanningLayout } from "./TguPlanningLayout";
@@ -25,7 +26,7 @@ export function TguPlanningPage() {
const [search, setSearch] = useState("");
const [selectedSpacecraftId, setSelectedSpacecraftId] = useState<string>();
const [selectedPlanId, setSelectedPlanId] = useState<string>();
const [activeTab, setActiveTab] = useState<"timeline" | "map">("timeline");
const [activeTab, setActiveTab] = useState<TguActiveTab>("timeline");
const [decisionInFlight, setDecisionInFlight] = useState(false);
const [decisionNotice, setDecisionNotice] = useState<string>();
const [decisionError, setDecisionError] = useState<string>();
@@ -197,7 +198,7 @@ export function TguPlanningPage() {
onClose={() => setSelectedPlanId(undefined)}
/>
</main>
) : (
) : activeTab === "map" ? (
<Tgu2DMapTab
range={appliedRange}
invalidRange={invalidRange}
@@ -210,6 +211,13 @@ export function TguPlanningPage() {
setDecisionNotice(undefined);
}}
/>
) : (
<TguEditorTab
selectedSpacecraftId={selectedPlan?.spacecraftId ?? selectedSpacecraftId}
selectedPlan={selectedPlan}
appliedRange={appliedRange}
platforms={platforms}
/>
)}
</TguPlanningLayout>
);
@@ -1,12 +1,13 @@
import { TguLegend } from "./TguLegend";
import type { TguActiveTab } from "../../model/timelineTypes";
type TguToolbarProps = {
fromValue: string;
toValue: string;
loading: boolean;
activeTab: "timeline" | "map";
activeTab: TguActiveTab;
error?: string;
onTabChange: (tab: "timeline" | "map") => void;
onTabChange: (tab: TguActiveTab) => void;
onFromChange: (value: string) => void;
onToChange: (value: string) => void;
onApply: () => void;
@@ -57,6 +58,15 @@ export function TguToolbar({
>
Карта 2D
</button>
<button
className={activeTab === "editor" ? "is-active" : ""}
type="button"
role="tab"
aria-selected={activeTab === "editor"}
onClick={() => onTabChange("editor")}
>
Редактор
</button>
</div>
<label className="tgu-field">