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:
@@ -0,0 +1,82 @@
|
||||
import type { TguPlanUi, TguPlatformUi } from "../../model/timelineTypes";
|
||||
import type { Draft, EditorWork } from "./model/editorTypes";
|
||||
|
||||
type EditorRailProps = {
|
||||
selectedSpacecraftId?: string;
|
||||
selectedPlan?: TguPlanUi;
|
||||
platforms: TguPlatformUi[];
|
||||
draft: Draft;
|
||||
conflictCount: number;
|
||||
contextSpacecraftIds: Set<string>;
|
||||
onToggleContext: (spacecraftId: string) => void;
|
||||
};
|
||||
|
||||
export function EditorRail({
|
||||
selectedSpacecraftId,
|
||||
selectedPlan,
|
||||
platforms,
|
||||
draft,
|
||||
conflictCount,
|
||||
contextSpacecraftIds,
|
||||
onToggleContext
|
||||
}: EditorRailProps) {
|
||||
const currentPlatform = platforms.find((platform) => platform.spacecraftId === selectedSpacecraftId);
|
||||
const contextPlatforms = platforms.filter((platform) => platform.spacecraftId !== selectedSpacecraftId).slice(0, 40);
|
||||
|
||||
return (
|
||||
<aside className="tgu-editor-rail">
|
||||
<section className="tgu-editor-rail__section">
|
||||
<div className="tgu-editor-eyebrow">Редактируется</div>
|
||||
<div className="tgu-editor-rail__title">{currentPlatform?.name ?? selectedSpacecraftId ?? "КА не выбран"}</div>
|
||||
<div className="tgu-editor-rail__meta">
|
||||
{selectedPlan ? `${selectedPlan.planId} · КПП ${selectedPlan.kppId}` : "План не выбран"}
|
||||
</div>
|
||||
|
||||
<div className="tgu-editor-stats">
|
||||
<RailStat label="Включений" value={draft.works.length} />
|
||||
<RailStat label="Съёмка" value={countWorks(draft.works, "shoot")} color="var(--t-optical)" />
|
||||
<RailStat label="Сброс" value={countWorks(draft.works, "downlink")} color="var(--accent)" />
|
||||
<RailStat label="Конфл." value={conflictCount} color={conflictCount > 0 ? "var(--now)" : undefined} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="tgu-editor-rail__section">
|
||||
<div className="tgu-editor-eyebrow">Контекст других КА</div>
|
||||
<div className="tgu-editor-context-list">
|
||||
{contextPlatforms.length === 0 ? (
|
||||
<div className="tgu-empty tgu-empty--compact">Нет других КА в выбранном интервале.</div>
|
||||
) : (
|
||||
contextPlatforms.map((platform) => {
|
||||
const enabled = contextSpacecraftIds.has(platform.spacecraftId);
|
||||
return (
|
||||
<button
|
||||
className={`tgu-editor-context-item ${enabled ? "is-enabled" : ""}`}
|
||||
key={platform.spacecraftId}
|
||||
type="button"
|
||||
onClick={() => onToggleContext(platform.spacecraftId)}
|
||||
>
|
||||
<span className="tgu-editor-context-item__check">{enabled ? "✓" : ""}</span>
|
||||
<span>{platform.name ?? platform.noradId ?? platform.spacecraftId}</span>
|
||||
<small>{platform.planCount}</small>
|
||||
</button>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
function RailStat({ label, value, color }: { label: string; value: number; color?: string }) {
|
||||
return (
|
||||
<div className="tgu-editor-stat">
|
||||
<span style={{ color }}>{value}</span>
|
||||
<small>{label}</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function countWorks(works: EditorWork[], kind: EditorWork["kind"]): number {
|
||||
return works.filter((work) => work.kind === kind).length;
|
||||
}
|
||||
Reference in New Issue
Block a user