feat(geo): AOI picker — Leaflet draw → bbox/polygon фильтр записей
Admin кликает 'AOI фильтр' на странице словаря → открывается dialog с картой OSM. Рисует rectangle (→ bbox CSV) или polygon → onApply применяется к recordsQuery как ?bbox= или ?polygon=GeoJSON. Server-side parsing уже есть (BoundingBox.parse, GeoJsonPolygon.parse в ordinis-rest-api) — подключили UI поверх. Активный AOI отображается inline над таблицей с кнопкой 'Сбросить'. Edit shape возможен через leaflet-draw edit/remove controls. Зависимости: leaflet 1.9.4 + react-leaflet 5 + leaflet-draw 1.0.4 + types. Bundle +231KB gzip 62KB — приемлемо для AOI-only feature (map работает без MapLibre WASM, OSM tiles — публичные). Picker self-contained: монтируется/уничтожается с диалогом, invalidateSize 100ms после open чтобы Leaflet корректно посчитал container size после modal animation.
This commit is contained in:
@@ -30,13 +30,33 @@ export const dictionaryDetailQuery = (name: string) =>
|
||||
},
|
||||
})
|
||||
|
||||
export const recordsQuery = (dictionaryName: string, scopeCsv: string) =>
|
||||
export type RecordsFilter = {
|
||||
/** CSV bbox: west,south,east,north */
|
||||
bbox?: string
|
||||
/** GeoJSON Polygon as raw object — serialized as JSON в query string. */
|
||||
polygon?: GeoJSON.Polygon
|
||||
}
|
||||
|
||||
export const recordsQuery = (
|
||||
dictionaryName: string,
|
||||
scopeCsv: string,
|
||||
filter?: RecordsFilter,
|
||||
) =>
|
||||
queryOptions({
|
||||
queryKey: ['records', dictionaryName, scopeCsv] as const,
|
||||
queryKey: [
|
||||
'records',
|
||||
dictionaryName,
|
||||
scopeCsv,
|
||||
filter?.bbox ?? null,
|
||||
filter?.polygon ? JSON.stringify(filter.polygon) : null,
|
||||
] as const,
|
||||
queryFn: async (): Promise<FlattenedRecord[]> => {
|
||||
const params: Record<string, string> = { as_scope: scopeCsv }
|
||||
if (filter?.bbox) params.bbox = filter.bbox
|
||||
if (filter?.polygon) params.polygon = JSON.stringify(filter.polygon)
|
||||
const { data } = await apiClient.get<FlattenedRecord[]>(
|
||||
`/${dictionaryName}/records`,
|
||||
{ params: { as_scope: scopeCsv } },
|
||||
{ params },
|
||||
)
|
||||
return data
|
||||
},
|
||||
@@ -235,8 +255,11 @@ export const useWebhookDlq = (page: number, size: number) =>
|
||||
|
||||
export const useDictionaries = () => useQuery(dictionariesQuery)
|
||||
export const useDictionaryDetail = (name: string) => useQuery(dictionaryDetailQuery(name))
|
||||
export const useRecords = (dictionaryName: string, scopeCsv: string) =>
|
||||
useQuery(recordsQuery(dictionaryName, scopeCsv))
|
||||
export const useRecords = (
|
||||
dictionaryName: string,
|
||||
scopeCsv: string,
|
||||
filter?: RecordsFilter,
|
||||
) => useQuery(recordsQuery(dictionaryName, scopeCsv, filter))
|
||||
export const useRecordRaw = (
|
||||
dictionaryName: string,
|
||||
businessKey: string | undefined,
|
||||
|
||||
Reference in New Issue
Block a user