feat(geoportal): Faza E v2 thin client (PMTiles + gis.ac)
New geoportal module flag-gated by session.useGisAc. Legacy code path preserved as GeoportalV1Legacy (rename only — same logic). When session.useGisAc=true (Infisical USE_GIS_AC=1 OR email in GIS_AC_PILOT_USERS), the page renders GeoportalV2 instead. V2 layout (851 LOC across 5 files): - map-viewer.tsx (~295 LOC): MapLibre + PMTiles src `pmtiles://pmtiles.gis.ac/overview.pmtiles`. Layers: UAT boundaries (z5, z8), parcels (gis_terenuri line + invisible hit-test fill), buildings (gis_cladiri fill+line). Click → resolves layerId from sourceLayer, emits ClickedFeatureLite (id, siruta, cadastralRef, layerId). - search-bar.tsx (~160 LOC): debounced 300ms, calls /api/gis/search, dropdown grouped by UATs / Parcele. - feature-info-panel.tsx (~270 LOC): fetches /api/gis/parcela/[id], renders enrichment block (scope-aware — 403 shown explicitly as "permisiuni insuficiente"). Buttons: "Citește din ANCPI" (POST /api/gis/parcel/tech force:true), "Export GeoPackage" (deep-link to eterra.live/harta?…&autoexport=geopackage), "Comandă CF" placeholder pending Faza F. - basemap-switcher.tsx (~40 LOC): liberty / dark / satellite / google. Dropped orto + topo50/25 (require ANCPI session — orto/topo via raster.gis.ac is TBD Sprint 2). - geoportal-v2.tsx (~85 LOC): wraps MapViewer + SearchBar + BasemapSwitcher + FeatureInfoPanel. API routes (90 LOC across 3 files): - GET /api/gis/search → gisApi.search - GET /api/gis/parcela/[id] → gisApi.parcela.get - POST /api/gis/parcel/tech → gisApi.parcel.tech (refresh ANCPI) All routes 401 if no NextAuth session, forward GisApiError status+code, hit api.gis.ac with the Authentik access_token from session. Per project_audit_correlation_echo memory: no correlationId set on client side (gis-api overwrites server-side). Cutover-bottom-right badge "gis.ac · v2" visible until full rollout for ops visibility. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState, useRef, useCallback } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { LayerPanel, getDefaultVisibility } from "./layer-panel";
|
||||
import { BasemapSwitcher } from "./basemap-switcher";
|
||||
import { SearchBar } from "./search-bar";
|
||||
@@ -12,6 +13,7 @@ import type { MapViewerHandle } from "./map-viewer";
|
||||
import type {
|
||||
BasemapId, ClickedFeature, LayerVisibility, SearchResult, SelectedFeature,
|
||||
} from "../types";
|
||||
import { GeoportalV2 } from "../v2/geoportal-v2";
|
||||
|
||||
const MapViewer = dynamic(
|
||||
() => import("./map-viewer").then((m) => ({ default: m.MapViewer })),
|
||||
@@ -26,6 +28,18 @@ const MapViewer = dynamic(
|
||||
);
|
||||
|
||||
export function GeoportalModule() {
|
||||
// Faza C cutover — branch on session.useGisAc (server-decided per request,
|
||||
// exposed via NextAuth session callback in src/core/auth/auth-options.ts).
|
||||
// Flag flip = Infisical USE_GIS_AC=1 + container force-recreate.
|
||||
const { data: session } = useSession();
|
||||
const useGisAc = Boolean((session as { useGisAc?: boolean } | null)?.useGisAc);
|
||||
if (useGisAc) {
|
||||
return <GeoportalV2 />;
|
||||
}
|
||||
return <GeoportalV1Legacy />;
|
||||
}
|
||||
|
||||
function GeoportalV1Legacy() {
|
||||
const mapHandleRef = useRef<MapViewerHandle>(null);
|
||||
const [basemap, setBasemap] = useState<BasemapId>("liberty");
|
||||
const [layerVisibility, setLayerVisibility] = useState<LayerVisibility>(getDefaultVisibility);
|
||||
|
||||
Reference in New Issue
Block a user