diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanDetails.tsx b/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanDetails.tsx
index 3ae267b..91b05bd 100644
--- a/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanDetails.tsx
+++ b/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanDetails.tsx
@@ -11,8 +11,16 @@ import { formatLocalDateTime, parseLocal } from "../../utils/localTime";
// config-repo/pcp-tgu-service.yaml → tgu.planning.waiting-decision-grace-minutes.
const DECISION_GRACE_MINUTES = 30;
+/** Состав включений плана: сколько съёмок и сбросов. undefined — ещё грузится / неизвестно. */
+export type PlanModeCounts = {
+ survey: number;
+ drop: number;
+};
+
type TguPlanDetailsProps = {
plan?: TguPlanUi;
+ /** Состав включений плана для блока «Включения» (съёмки/сбросы). */
+ modeCounts?: PlanModeCounts;
/** Цепочка КА на паузе после REJECTED — показываем подсказку про ручной исправленный план. */
chainPaused?: boolean;
decisionInFlight: boolean;
@@ -29,6 +37,7 @@ type TguPlanDetailsProps = {
export function TguPlanDetails({
plan,
+ modeCounts,
chainPaused,
decisionInFlight,
notice,
@@ -95,6 +104,13 @@ export function TguPlanDetails({
+ {plan.basePlanId && }
+
+
+
Включения
+
+
+
{plan.status === "DRAFT" && (
diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanningPage.tsx b/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanningPage.tsx
index 368888d..75d0b68 100644
--- a/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanningPage.tsx
+++ b/services/pcp-tgu-ops-ui/src/features/tgu-planning/TguPlanningPage.tsx
@@ -12,8 +12,9 @@ import {
promoteDraft,
type ZrvWindowResponse
} from "../../api/tguPlanApi";
+import { fetchPlanModes } from "../tgu-editor/editorApi";
import { CreateChainPlanDialog } from "./CreateChainPlanDialog";
-import { TguPlanDetails } from "./TguPlanDetails";
+import { TguPlanDetails, type PlanModeCounts } from "./TguPlanDetails";
import { TguPlanningLayout } from "./TguPlanningLayout";
import { TguSidebar } from "./TguSidebar";
import { TguTimeline, type ZrvWindow } from "./TguTimeline";
@@ -208,6 +209,32 @@ export function TguPlanningPage() {
return { ...plan, platform };
}, [allPlans, platforms, selectedPlanId]);
+ // Состав включений выбранного плана (счётчики съёмок/сбросов) для правой панели.
+ // planId == mission_id, поэтому грузим режимы плана тем же эндпоинтом, что и редактор.
+ const [planModeCounts, setPlanModeCounts] = useState();
+ useEffect(() => {
+ if (!selectedPlanId) {
+ setPlanModeCounts(undefined);
+ return;
+ }
+ let cancelled = false;
+ setPlanModeCounts(undefined);
+ void fetchPlanModes(selectedPlanId)
+ .then((modes) => {
+ if (cancelled) return;
+ setPlanModeCounts({
+ survey: modes.filter((mode) => mode.type === "SURVEY").length,
+ drop: modes.filter((mode) => mode.type === "DROP").length
+ });
+ })
+ .catch(() => {
+ if (!cancelled) setPlanModeCounts(undefined);
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [selectedPlanId]);
+
const applyRange = () => {
setDecisionNotice(undefined);
if (invalidRange) {
@@ -351,6 +378,8 @@ export function TguPlanningPage() {
selectedPlanId={selectedPlanId}
onSelectPlan={(planId) => {
setZrvSelection(undefined);
+ setDecisionNotice(undefined);
+ setDecisionError(undefined);
setSelectedPlanId(planId);
const plan = allPlans.find((item) => item.planId === planId);
if (plan) {
@@ -359,6 +388,8 @@ export function TguPlanningPage() {
}}
onSelectZrv={(windows, index) => {
setSelectedPlanId(undefined);
+ setDecisionNotice(undefined);
+ setDecisionError(undefined);
setZrvSelection({ windows, index });
}}
/>
@@ -373,6 +404,7 @@ export function TguPlanningPage() {
) : (
new Map(segments.map((segment) => [segment.plan.planId, segment])),
- [segments]
- );
- const links = useMemo(() => buildSequentialLinks(rows), [rows]);
+ // basePlanId выбранного плана — чтобы подсветить его прошлое/будущее звено по РЕАЛЬНОЙ связи
+ // (а не по времени): предыдущий = план с этим planId, следующий = план с basePlanId == выбранный.
+ const selectedBasePlanId = selectedPlanId
+ ? segments.find((segment) => segment.plan.planId === selectedPlanId)?.plan.basePlanId ?? undefined
+ : undefined;
const ticks = useMemo(() => buildTimeTicks(range), [range]);
const nowX =
nowMs >= range.fromMs && nowMs <= range.toMs
@@ -95,12 +94,6 @@ export function TguTimeline({
Нет планов в выбранном интервале.
) : (