2D-карта (станции, заявки) и вкладка «Заявки»

Новая вкладка «Заявки»: показ заявок за интервал на 2D-карте,
создание заявки рисованием полигона на карте с формой параметров
(Оптика/РСА) и удаление выбранной заявки (createRequest/deleteRequest,
RequestCreatePanel/RequestDetailsPanel, polygonToWkt). В Tgu2DMapView
добавлен изолированный режим рисования полигона.

2D-карта: панели информации о станции и о заявках, слой станций
(stationsApi), поддержка выбора объектов через спатиальный индекс.
Список слоёв упрощён — трасса/полоса/маркеры КА убраны из переключателя,
по умолчанию включены работы плана, станции и заявки.
This commit is contained in:
Дмитрий Соловьев
2026-06-01 22:17:50 +03:00
parent 715b5108d6
commit 0128c5569c
26 changed files with 1851 additions and 96 deletions
+41 -1
View File
@@ -1,4 +1,4 @@
import type { RequestFilter, RequestMapItem } from "../model/requestTypes";
import type { CreateRequestPayload, CreateRequestResult, RequestFilter, RequestMapItem } from "../model/requestTypes";
const BASE = "/api/pcp-request/v1";
const PAGE_SIZE = 500;
@@ -60,3 +60,43 @@ export async function fetchRequestsForMap(
truncated: first.totalPages > MAX_PAGES
};
}
type ErrorDetail = { field?: string; message?: string };
type ErrorBody = { code?: string; message?: string; details?: ErrorDetail[] };
async function readErrorMessage(response: Response, fallback: string): Promise<string> {
try {
const body = (await response.json()) as ErrorBody;
const details = body.details?.map((d) => (d.field ? `${d.field}: ${d.message}` : d.message)).filter(Boolean);
const detailText = details && details.length > 0 ? ` (${details.join("; ")})` : "";
if (body.message) return `${body.message}${detailText}`;
} catch {
// тело ответа не JSON — используем fallback
}
return `${fallback}: ${response.status}`;
}
export async function createRequest(payload: CreateRequestPayload): Promise<CreateRequestResult> {
const response = await fetch(`${BASE}/requests`, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(await readErrorMessage(response, "Не удалось создать заявку"));
}
return response.json() as Promise<CreateRequestResult>;
}
export async function deleteRequest(id: string): Promise<void> {
const response = await fetch(`${BASE}/requests/${encodeURIComponent(id)}`, {
method: "DELETE",
headers: { Accept: "application/json" }
});
if (!response.ok) {
throw new Error(await readErrorMessage(response, "Не удалось удалить заявку"));
}
}
@@ -0,0 +1,18 @@
export type StationDto = {
id: string | null;
number: number;
name: string;
position: { lat: number; long: number; height: number };
elevationMin: number;
elevationMax: number;
};
export async function fetchStations(): Promise<StationDto[]> {
const response = await fetch("/api/stations", {
headers: { Accept: "application/json" }
});
if (!response.ok) {
throw new Error(`Ошибка загрузки станций: ${response.status}`);
}
return response.json() as Promise<StationDto[]>;
}