Files
dc-observatio/services/pcp-tgu-ui-service/src/features/tgu-slots/CoveragePanel.tsx
T
Дмитрий Соловьев 354730fb6c feat(tgu-ui): запрет выбора конфликтных слотов + прохождение без подсветки
- конфликтные слоты нельзя добавить в выбор под бронь (клик по строке,
  Shift-диапазон, выбор полосы на карте, «выбрать все») — жёсткая блокировка;
  снятие выбора не блокируется (конфликт считается только для невыбранных)
- слоты одной полосы между собой не конфликтуют (groupOf) — иначе нельзя было
  бы набрать полосу поштучным кликом
- чекбокс «Конфликты» теперь и гасит, и блокирует выбор; выкл. — выбирать любые
- переход на прохождение (◀/▶) больше не подсвечивает никакой слот: активная
  подсветка строки — только по явному клику; добавлена прокрутка к началу прохода
- повторный Shift+клик по тому же концу снимает выделение (таблица и карта)
2026-06-17 14:07:03 +03:00

92 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 { CoverageResult } from "./coverage";
type CoveragePanelProps = {
/** Покрытие только забронированным набором. null — покрытие недоступно (нет геометрии/0 сэмплов). */
booked: CoverageResult | null;
/** Покрытие union(забронированное ∪ выбранное галочками). */
withSelection: CoverageResult | null;
/** Есть ли выбор сверх брони (показывать второй сегмент «с выбором»). */
hasSelection: boolean;
/** Геометрия заявки пригодна для расчёта (false → «недоступно»). */
available: boolean;
/** Тумблер следа покрытия на карте (D4 — локальный, вкл. по умолчанию). */
coverageOn: boolean;
onToggleCoverage: (on: boolean) => void;
/** Тумблер подсветки конфликтов выбора (тусклый слой) — рядом со следом на карте. */
showConflicts: boolean;
onToggleConflicts: (on: boolean) => void;
};
// Панель покрытия над списком слотов (D-D1): «забронировано NN% · с выбором MM%» + двухсегментный
// бар + явные состояния. Цвет = цвет слоя coverage на карте (violet) — единый язык «покрыто».
export function CoveragePanel({
booked,
withSelection,
hasSelection,
available,
coverageOn,
onToggleCoverage,
showConflicts,
onToggleConflicts,
}: CoveragePanelProps) {
const bookedPct = booked?.percent ?? 0;
const withPct = withSelection?.percent ?? bookedPct;
// Прирост от выбора (второй сегмент) — поверх забронированного.
const gainPct = Math.max(0, withPct - bookedPct);
const lowPrecision = !!(booked?.lowPrecision || withSelection?.lowPrecision);
return (
<div className="tgu-coverage">
<div className="tgu-coverage__head">
<span className="tgu-coverage__title">Покрытие заявки</span>
<div className="tgu-coverage__toggles">
<label className="tgu-coverage__toggle" title="Показывать след покрытия на карте">
<input
type="checkbox"
checked={coverageOn}
onChange={(event) => onToggleCoverage(event.target.checked)}
/>
След на карте
</label>
<label
className="tgu-coverage__toggle"
title="Гасить и блокировать выбор слотов, недоступных из-за текущего выбора (тот же КА, не хватает времени на манёвр). Выкл. — выбирать можно любые."
>
<input
type="checkbox"
checked={showConflicts}
onChange={(event) => onToggleConflicts(event.target.checked)}
/>
Конфликты
</label>
</div>
</div>
{!available ? (
<div className="tgu-coverage__unavailable">Покрытие недоступно у заявки нет геометрии.</div>
) : (
<>
<div className="tgu-coverage__bar" role="presentation">
<div className="tgu-coverage__seg tgu-coverage__seg--booked" style={{ width: `${bookedPct}%` }} />
{hasSelection && gainPct > 0 && (
<div className="tgu-coverage__seg tgu-coverage__seg--sel" style={{ width: `${gainPct}%` }} />
)}
</div>
<div className="tgu-coverage__legend">
<span className="tgu-coverage__booked">забронировано {bookedPct}%</span>
{hasSelection && (
<span className="tgu-coverage__sel"> · с выбором {withPct}%</span>
)}
{lowPrecision && (
<span className="tgu-coverage__low" title="Заявка слишком мала для надёжной оценки">
{" "}
· низкая точность
</span>
)}
</div>
</>
)}
</div>
);
}