pcp-tgu-ops-ui: вкладка Редактор — план, включения, нижняя панель
- Слева выводим даты начала/конца плана и корректные счётчики съёмки/сброса (из plan modes, а не из заглушки-черновика). - На таймлайне показываем реальные включения плана (read-only): убран фейковый «сброс на весь интервал»; вместо линии «сейчас» — маркеры начала и конца плана; клик по включению открывает его инфо справа. - Включения съёмки и сброса разнесены на два ряда (верх/низ) и различаются цветом, слева — подписи рядов. - Нижняя панель: активная дорожка резиновая — примыкает к низу и не пустует, при подъёме панели ряды расширяются; высота бара ограничена потолком и центрируется в ряду, чтобы узкие включения не вытягивались. - Время в редакторе парсится/форматируется через utils/utcTime.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import type { TguPlanUi } from "../../model/timelineTypes";
|
||||
import { parseUtc } from "../../utils/utcTime";
|
||||
import type { PlanMode } from "./editorApi";
|
||||
import type {
|
||||
Draft,
|
||||
DraftHistory,
|
||||
@@ -35,14 +37,53 @@ export function seedDraft(plan?: TguPlanUi, spacecraftId?: string): Draft {
|
||||
return { spacecraftId, works: [] };
|
||||
}
|
||||
|
||||
const embeddedWorks = readEmbeddedWorks(plan);
|
||||
return {
|
||||
planId: plan.planId,
|
||||
spacecraftId: plan.spacecraftId,
|
||||
works: embeddedWorks.length > 0 ? embeddedWorks : [fallbackPlanWork(plan)]
|
||||
// Черновик содержит только правки, внесённые в редакторе. Существующие включения
|
||||
// плана грузятся отдельно (fetchPlanModes) и показываются как read-only.
|
||||
works: readEmbeddedWorks(plan)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразует включения плана (plan modes из pcp-tgu-service) в работы для отображения
|
||||
* на таймлайне редактора. Это read-only представление фактического состава плана:
|
||||
* SURVEY → съёмка, DROP → сброс на НКПОР.
|
||||
*/
|
||||
export function planModesToWorks(modes: PlanMode[]): EditorWork[] {
|
||||
return modes.flatMap((mode): EditorWork[] => {
|
||||
if (mode.timeStart == null) return [];
|
||||
const startMs = parseUtc(mode.timeStart);
|
||||
if (!Number.isFinite(startMs)) return [];
|
||||
const endMs = startMs + Math.max(1, mode.duration) * 1000;
|
||||
|
||||
if (mode.type === "SURVEY") {
|
||||
return [{
|
||||
id: `survey-${mode.id}`,
|
||||
kind: "shoot",
|
||||
label: `Съёмка · виток ${mode.revolution}`,
|
||||
startMs,
|
||||
endMs,
|
||||
origin: "plan",
|
||||
targetLat: mode.lat,
|
||||
targetLon: mode.longitude
|
||||
}];
|
||||
}
|
||||
|
||||
return [{
|
||||
id: `drop-${mode.id}`,
|
||||
kind: "downlink",
|
||||
label: mode.station ? `Сброс · ${mode.station}` : "Сброс на НКПОР",
|
||||
startMs,
|
||||
endMs,
|
||||
origin: "plan",
|
||||
stationId: mode.station ?? undefined
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function mutate(
|
||||
history: DraftHistory,
|
||||
transform: (draft: Draft) => Draft,
|
||||
@@ -154,18 +195,6 @@ export function duplicateWork(
|
||||
return { ...draft, works: [...draft.works, duplicated] };
|
||||
}
|
||||
|
||||
function fallbackPlanWork(plan: TguPlanUi): EditorDownlinkWork {
|
||||
return {
|
||||
id: `${plan.planId}:plan-window`,
|
||||
kind: "downlink",
|
||||
label: `План ТГУ · ${plan.kppId}`,
|
||||
startMs: plan.startMs,
|
||||
endMs: plan.endMs,
|
||||
stationId: plan.kppId,
|
||||
origin: "plan"
|
||||
};
|
||||
}
|
||||
|
||||
function readEmbeddedWorks(plan: TguPlanUi): EditorWork[] {
|
||||
const rawWorks = (plan as TguPlanUi & { works?: unknown }).works;
|
||||
if (!Array.isArray(rawWorks)) {
|
||||
@@ -217,7 +246,7 @@ function readTime(record: Record<string, unknown>, key: string): number | undefi
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const parsed = new Date(value).getTime();
|
||||
const parsed = parseUtc(value);
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user