chore: сохранение текущих изменений (deploy local-bundle/local-jar, request-service, slots UI markedObjectIds)

Безопасный коммит незакоммиченной работы перед реализацией бронирования слотов.
Вне коммита оставлены: package-lock.json (пустой, мусор), requests.json (scratch-данные).
This commit is contained in:
Дмитрий Соловьев
2026-06-11 14:00:12 +03:00
parent 6689351ef0
commit 345529cde0
36 changed files with 1674 additions and 37 deletions
@@ -24,6 +24,8 @@ type Tgu2DMapViewProps = {
selectedRequestPolygonId?: string;
/** Внешняя подсветка объекта (контур съёмки или линия сброса) по его id — «как будто кликнули на карте». */
selectedObjectId?: string;
/** Отмеченные объекты (постоянная подсветка независимо от выбора) — напр. слоты, отобранные под бронь. */
markedObjectIds?: ReadonlySet<string>;
/** Точка, на которую центрировать карту; перецентрирование срабатывает при смене focusToken. */
focusPoint?: GeoPoint;
focusToken?: number;
@@ -84,6 +86,7 @@ export function Tgu2DMapView({
hoveredRequestPolygonId,
selectedRequestPolygonId,
selectedObjectId,
markedObjectIds,
focusPoint,
focusToken,
enableObjectChooser = false,
@@ -529,6 +532,14 @@ export function Tgu2DMapView({
[hoveredRequestPolygonId, activePolygons]
);
const overlayPolygons = uniquePolygons([externalSelectedPolygon, selectedPolygon, externalHoveredPolygon, hoverRef.current]);
// Отмеченные объекты (напр. отобранные под бронь слоты) — постоянная подсветка под обычным выбором.
const markedPolygons = useMemo(
() =>
markedObjectIds && markedObjectIds.size > 0
? activePolygons.filter((p) => markedObjectIds.has(p.id))
: [],
[markedObjectIds, activePolygons]
);
const overlayMarkers = uniqueMarkers([selectedMarker, hoverMarkerRef.current]);
// Накопленное расстояние (км) от первой вершины линейки до каждой — по дуге большого круга.
@@ -563,6 +574,24 @@ export function Tgu2DMapView({
>
<canvas className="tgu-2d-map__canvas" ref={canvasRef} />
<svg className="tgu-2d-map__overlay" width={size.width} height={size.height} aria-hidden="true">
{markedPolygons.flatMap((polygon) => {
const raw = polygon.points.map((point) => projection.project(point));
const pts = unwrapX(raw, projection.worldSize);
return ([0, projection.worldSize, -projection.worldSize] as const).flatMap((dx) => {
const visible = pts.some((p) => {
const x = p.x + dx;
return x >= -64 && x <= size.width + 64 && p.y >= -64 && p.y <= size.height + 64;
});
if (!visible) return [];
return (
<polygon
key={`marked:${polygon.id}:${dx}`}
points={pts.map((p) => `${p.x + dx},${p.y}`).join(" ")}
className="is-marked"
/>
);
});
})}
{overlayPolygons.flatMap((polygon) => {
const raw = polygon.points.map((point) => projection.project(point));
const pts = unwrapX(raw, projection.worldSize);
@@ -113,6 +113,12 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
// Ключи забронированных слотов — для бейджа «бронь» в таблице и отдельного слоя на карте.
const bookedKeys = useMemo(() => new Set(bookedSlots.map(slotKey)), [bookedSlots]);
// id полигонов слотов, отобранных галочками под бронь — постоянная подсветка на карте.
const markedSlotIds = useMemo(
() => new Set(Array.from(selectedKeys, (key) => `slot:${key}`)),
[selectedKeys]
);
// Слот по ключу — чтобы из выбора на карте (id "slot:key"/"booked:key") восстановить полные данные.
const slotByKey = useMemo(() => {
const map = new Map<string, SlotDTO>();
@@ -758,6 +764,7 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
onObjectsAtPoint={handleObjectsAtPoint}
onObjectSelect={handleObjectSelect}
selectedObjectId={hasRequest ? activeSlotId : activeHitId}
markedObjectIds={hasRequest ? markedSlotIds : undefined}
focusPoint={focusPoint}
focusToken={focusToken}
/>