zenit-sdk 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -298,6 +298,26 @@ var import_react_leaflet = require("react-leaflet");
298
298
  var import_leaflet = __toESM(require("leaflet"));
299
299
  var import_jsx_runtime = require("react/jsx-runtime");
300
300
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
301
+ function normalizeBboxFromData(data) {
302
+ const bboxCandidate = data.bbox;
303
+ if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
304
+ const [minLon, minLat, maxLon, maxLat] = bboxCandidate;
305
+ const values = [minLon, minLat, maxLon, maxLat].map((value) => Number(value));
306
+ if (values.every((value) => Number.isFinite(value))) {
307
+ return values;
308
+ }
309
+ return null;
310
+ }
311
+ function buildIdsSample(features) {
312
+ return features.slice(0, 10).map((feature) => {
313
+ const typedFeature = feature;
314
+ if (typedFeature.id !== void 0 && typedFeature.id !== null) {
315
+ return String(typedFeature.id);
316
+ }
317
+ const featureId = typedFeature.properties?.featureId;
318
+ return featureId !== void 0 && featureId !== null ? String(featureId) : "";
319
+ }).join(",");
320
+ }
301
321
  function getGeometryType(feature) {
302
322
  const t = feature?.geometry?.type;
303
323
  return typeof t === "string" ? t : null;
@@ -333,6 +353,18 @@ var LayerGeoJson = ({
333
353
  const features = data.features ?? [];
334
354
  const fillFeatures = features.filter(isNonPointGeometry);
335
355
  const pointFeatures = features.filter(isPointGeometry);
356
+ const dataVersionRef = (0, import_react.useRef)(0);
357
+ const prevSignatureRef = (0, import_react.useRef)("");
358
+ const firstId = features.length > 0 ? String(features[0]?.id ?? "") : "";
359
+ const lastId = features.length > 0 ? String(features[features.length - 1]?.id ?? "") : "";
360
+ const bbox = normalizeBboxFromData(data);
361
+ const idsSample = buildIdsSample(features);
362
+ const signature = bbox ? `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}|${bbox[0]}|${bbox[1]}|${bbox[2]}|${bbox[3]}` : `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}`;
363
+ const signatureToken = signature.replace(/[^a-zA-Z0-9_-]/g, "_");
364
+ if (prevSignatureRef.current !== signature) {
365
+ dataVersionRef.current += 1;
366
+ prevSignatureRef.current = signature;
367
+ }
336
368
  const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
337
369
  const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
338
370
  const clusterLayerRef = (0, import_react.useRef)(null);
@@ -385,7 +417,7 @@ var LayerGeoJson = ({
385
417
  onPolygonLabel?.(feature, layer);
386
418
  }
387
419
  },
388
- `fill-${layerId}`
420
+ `fill-${layerId}-${signatureToken}-v${dataVersionRef.current}`
389
421
  ),
390
422
  pointsData && !canCluster && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
391
423
  import_react_leaflet.GeoJSON,
@@ -398,7 +430,7 @@ var LayerGeoJson = ({
398
430
  }),
399
431
  onEachFeature
400
432
  },
401
- `points-${layerId}`
433
+ `points-${layerId}-${signatureToken}-v${dataVersionRef.current}`
402
434
  )
403
435
  ] });
404
436
  };
@@ -958,6 +990,18 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
958
990
  // src/react/map/map-handlers.tsx
959
991
  var import_react4 = require("react");
960
992
  var import_react_leaflet3 = require("react-leaflet");
993
+ var MapInvalidator = ({ trigger }) => {
994
+ const map = (0, import_react_leaflet3.useMap)();
995
+ const lastTrigger = (0, import_react4.useRef)(void 0);
996
+ (0, import_react4.useEffect)(() => {
997
+ if (lastTrigger.current === trigger) return;
998
+ lastTrigger.current = trigger;
999
+ requestAnimationFrame(() => {
1000
+ map.invalidateSize();
1001
+ });
1002
+ }, [map, trigger]);
1003
+ return null;
1004
+ };
961
1005
  function computeBBoxFromGeojson(geojson) {
962
1006
  if (!geojson || !Array.isArray(geojson.features)) return null;
963
1007
  const coords = [];
@@ -1186,6 +1230,8 @@ var ZenitMap = (0, import_react5.forwardRef)(({
1186
1230
  const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
1187
1231
  const [mapError, setMapError] = (0, import_react5.useState)(null);
1188
1232
  const [mapInstance, setMapInstance] = (0, import_react5.useState)(null);
1233
+ const [layerGeojsonOverrides, setLayerGeojsonOverrides] = (0, import_react5.useState)({});
1234
+ const originalGeojsonByLayerIdRef = (0, import_react5.useRef)({});
1189
1235
  const [panesReady, setPanesReady] = (0, import_react5.useState)(false);
1190
1236
  const [currentZoom, setCurrentZoom] = (0, import_react5.useState)(initialZoom ?? DEFAULT_ZOOM);
1191
1237
  const [isPopupOpen, setIsPopupOpen] = (0, import_react5.useState)(false);
@@ -1471,13 +1517,27 @@ var ZenitMap = (0, import_react5.forwardRef)(({
1471
1517
  (0, import_react5.useEffect)(() => {
1472
1518
  setCurrentZoom(zoom);
1473
1519
  }, [zoom]);
1520
+ (0, import_react5.useEffect)(() => {
1521
+ if (!layerGeojson) return;
1522
+ layers.forEach((layer) => {
1523
+ const layerKey = String(layer.mapLayer.layerId);
1524
+ const incoming = layerGeojson[layer.mapLayer.layerId] ?? layerGeojson[layerKey] ?? null;
1525
+ if (incoming && !originalGeojsonByLayerIdRef.current[layerKey]) {
1526
+ originalGeojsonByLayerIdRef.current[layerKey] = incoming;
1527
+ }
1528
+ });
1529
+ }, [layerGeojson, layers]);
1474
1530
  const decoratedLayers = (0, import_react5.useMemo)(() => {
1475
- return layers.map((layer) => ({
1476
- ...layer,
1477
- effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1478
- data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1479
- }));
1480
- }, [effectiveStates, layerGeojson, layers]);
1531
+ return layers.map((layer) => {
1532
+ const layerKey = String(layer.mapLayer.layerId);
1533
+ const override = layerGeojsonOverrides[layerKey];
1534
+ return {
1535
+ ...layer,
1536
+ effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1537
+ data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
1538
+ };
1539
+ });
1540
+ }, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
1481
1541
  const orderedLayers = (0, import_react5.useMemo)(() => {
1482
1542
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1483
1543
  }, [decoratedLayers]);
@@ -1706,6 +1766,24 @@ var ZenitMap = (0, import_react5.forwardRef)(({
1706
1766
  };
1707
1767
  mapInstance.fitBounds(bounds, fitOptions);
1708
1768
  },
1769
+ fitToBbox: (bbox, padding) => {
1770
+ if (!mapInstance) return;
1771
+ if (typeof bbox.minLat !== "number" || typeof bbox.minLon !== "number" || typeof bbox.maxLat !== "number" || typeof bbox.maxLon !== "number" || !Number.isFinite(bbox.minLat) || !Number.isFinite(bbox.minLon) || !Number.isFinite(bbox.maxLat) || !Number.isFinite(bbox.maxLon)) {
1772
+ console.warn("[ZenitMap.fitToBbox] Invalid bbox", bbox);
1773
+ return;
1774
+ }
1775
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1776
+ const bounds = import_leaflet4.default.latLngBounds([bbox.minLat, bbox.minLon], [bbox.maxLat, bbox.maxLon]);
1777
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1778
+ },
1779
+ fitToGeoJson: (fc, padding) => {
1780
+ if (!mapInstance) return;
1781
+ if (!fc?.features?.length) return;
1782
+ const bounds = import_leaflet4.default.geoJSON(fc).getBounds();
1783
+ if (!bounds.isValid()) return;
1784
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1785
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1786
+ },
1709
1787
  setView: (coordinates, zoom2) => {
1710
1788
  if (!mapInstance) return;
1711
1789
  mapInstance.setView([coordinates.lat, coordinates.lon], zoom2 ?? mapInstance.getZoom(), {
@@ -1730,8 +1808,25 @@ var ZenitMap = (0, import_react5.forwardRef)(({
1730
1808
  highlightFeature: (layerId, featureId) => {
1731
1809
  upsertUiOverride(layerId, { overrideVisible: true, overrideOpacity: 1 });
1732
1810
  },
1811
+ updateLayerGeoJson: (layerId, featureCollection) => {
1812
+ const layerKey = String(layerId);
1813
+ setLayerGeojsonOverrides((prev) => ({ ...prev, [layerKey]: featureCollection }));
1814
+ },
1815
+ restoreLayerGeoJson: (layerId) => {
1816
+ const layerKey = String(layerId);
1817
+ const original = originalGeojsonByLayerIdRef.current[layerKey];
1818
+ setLayerGeojsonOverrides((prev) => {
1819
+ const next = { ...prev };
1820
+ if (original) {
1821
+ next[layerKey] = original;
1822
+ } else {
1823
+ delete next[layerKey];
1824
+ }
1825
+ return next;
1826
+ });
1827
+ },
1733
1828
  getMapInstance: () => mapInstance
1734
- }), [effectiveStates, mapInstance]);
1829
+ }), [effectiveStates, isMobile, mapInstance]);
1735
1830
  if (loadingMap) {
1736
1831
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1737
1832
  }
@@ -1784,6 +1879,7 @@ var ZenitMap = (0, import_react5.forwardRef)(({
1784
1879
  ),
1785
1880
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_leaflet4.ZoomControl, { position: "topright" }),
1786
1881
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInstanceBridge, { onReady: handleMapReady }),
1882
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInvalidator, { trigger: mapId }),
1787
1883
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1788
1884
  BBoxZoomHandler,
1789
1885
  {
@@ -2001,7 +2097,9 @@ var ZenitLayerManager = ({
2001
2097
  showUploadTab = true,
2002
2098
  showLayerVisibilityIcon = true,
2003
2099
  layerFeatureCounts,
2004
- mapLayers
2100
+ mapLayers,
2101
+ onApplyLayerFilter,
2102
+ onClearLayerFilter
2005
2103
  }) => {
2006
2104
  const [map, setMap] = (0, import_react6.useState)(null);
2007
2105
  const [loadingMap, setLoadingMap] = (0, import_react6.useState)(false);
@@ -2009,6 +2107,15 @@ var ZenitLayerManager = ({
2009
2107
  const [layers, setLayers] = (0, import_react6.useState)([]);
2010
2108
  const [activeTab, setActiveTab] = (0, import_react6.useState)("layers");
2011
2109
  const [panelVisible, setPanelVisible] = (0, import_react6.useState)(true);
2110
+ const [selectedFilterLayerId, setSelectedFilterLayerId] = (0, import_react6.useState)("");
2111
+ const [selectedFilterField, setSelectedFilterField] = (0, import_react6.useState)("");
2112
+ const [selectedFilterValue, setSelectedFilterValue] = (0, import_react6.useState)("");
2113
+ const [catalogByLayerField, setCatalogByLayerField] = (0, import_react6.useState)({});
2114
+ const [loadingCatalog, setLoadingCatalog] = (0, import_react6.useState)(false);
2115
+ const [applyingFilter, setApplyingFilter] = (0, import_react6.useState)(false);
2116
+ const [filterError, setFilterError] = (0, import_react6.useState)(null);
2117
+ const [appliedFilter, setAppliedFilter] = (0, import_react6.useState)(null);
2118
+ const catalogAbortRef = (0, import_react6.useRef)(null);
2012
2119
  const lastEmittedStatesRef = (0, import_react6.useRef)(null);
2013
2120
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
2014
2121
  const baseStates = (0, import_react6.useMemo)(
@@ -2202,6 +2309,139 @@ var ZenitLayerManager = ({
2202
2309
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
2203
2310
  });
2204
2311
  }, [effectiveStates, layers, resolveFeatureCount]);
2312
+ const filterableLayers = (0, import_react6.useMemo)(() => {
2313
+ return decoratedLayers.filter((entry) => {
2314
+ const prefilters = entry.mapLayer.layerConfig?.prefilters;
2315
+ return !!prefilters && Object.keys(prefilters).length > 0;
2316
+ });
2317
+ }, [decoratedLayers]);
2318
+ const selectedFilterLayer = (0, import_react6.useMemo)(
2319
+ () => filterableLayers.find((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId) ?? null,
2320
+ [filterableLayers, selectedFilterLayerId]
2321
+ );
2322
+ const filterFields = (0, import_react6.useMemo)(() => {
2323
+ const prefilters = selectedFilterLayer?.mapLayer.layerConfig?.prefilters;
2324
+ return prefilters ? Object.keys(prefilters) : [];
2325
+ }, [selectedFilterLayer]);
2326
+ const activeCatalogKey = selectedFilterLayer ? `${selectedFilterLayer.mapLayer.layerId}:${selectedFilterField}` : null;
2327
+ const activeCatalogValues = activeCatalogKey ? catalogByLayerField[activeCatalogKey] ?? [] : [];
2328
+ const extractCatalogValues = import_react6.default.useCallback((catalogData, field) => {
2329
+ const values = /* @__PURE__ */ new Set();
2330
+ const pushValue = (value) => {
2331
+ if (value === null || value === void 0) return;
2332
+ const normalized = String(value).trim();
2333
+ if (normalized) values.add(normalized);
2334
+ };
2335
+ if (catalogData && typeof catalogData === "object") {
2336
+ const maybeRecord = catalogData;
2337
+ const directField = maybeRecord[field];
2338
+ if (Array.isArray(directField)) {
2339
+ directField.forEach(pushValue);
2340
+ }
2341
+ const items = maybeRecord.items;
2342
+ if (Array.isArray(items)) {
2343
+ items.forEach((item) => {
2344
+ if (!item || typeof item !== "object") return;
2345
+ const row = item;
2346
+ const rowField = row.field;
2347
+ if (String(rowField ?? "").toUpperCase() === field.toUpperCase() && Array.isArray(row.values)) {
2348
+ row.values.forEach(pushValue);
2349
+ }
2350
+ });
2351
+ }
2352
+ const features = maybeRecord.features;
2353
+ if (Array.isArray(features)) {
2354
+ features.forEach((feature) => {
2355
+ if (!feature || typeof feature !== "object") return;
2356
+ const properties = feature.properties;
2357
+ if (properties && field in properties) {
2358
+ pushValue(properties[field]);
2359
+ }
2360
+ });
2361
+ }
2362
+ }
2363
+ return [...values].sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
2364
+ }, []);
2365
+ (0, import_react6.useEffect)(() => {
2366
+ if (!filterableLayers.length) {
2367
+ setSelectedFilterLayerId("");
2368
+ return;
2369
+ }
2370
+ if (!selectedFilterLayerId || !filterableLayers.some((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId)) {
2371
+ setSelectedFilterLayerId(String(filterableLayers[0].mapLayer.layerId));
2372
+ }
2373
+ }, [filterableLayers, selectedFilterLayerId]);
2374
+ (0, import_react6.useEffect)(() => {
2375
+ if (!filterFields.length) {
2376
+ setSelectedFilterField("");
2377
+ return;
2378
+ }
2379
+ if (!selectedFilterField || !filterFields.includes(selectedFilterField)) {
2380
+ setSelectedFilterField(filterFields[0]);
2381
+ setSelectedFilterValue("");
2382
+ }
2383
+ }, [filterFields, selectedFilterField]);
2384
+ (0, import_react6.useEffect)(() => {
2385
+ if (activeTab !== "filters") return;
2386
+ if (!selectedFilterLayer || !selectedFilterField || !activeCatalogKey) return;
2387
+ if (catalogByLayerField[activeCatalogKey]) return;
2388
+ catalogAbortRef.current?.abort();
2389
+ const controller = new AbortController();
2390
+ catalogAbortRef.current = controller;
2391
+ setLoadingCatalog(true);
2392
+ setFilterError(null);
2393
+ client.layers.getLayerFeaturesCatalog(selectedFilterLayer.mapLayer.layerId).then((response) => {
2394
+ if (controller.signal.aborted) return;
2395
+ const values = extractCatalogValues(response.data, selectedFilterField);
2396
+ setCatalogByLayerField((prev) => ({ ...prev, [activeCatalogKey]: values }));
2397
+ }).catch((error) => {
2398
+ if (controller.signal.aborted) return;
2399
+ const message = error instanceof Error ? error.message : "No se pudo cargar el cat\xE1logo";
2400
+ setFilterError(message);
2401
+ }).finally(() => {
2402
+ if (!controller.signal.aborted) setLoadingCatalog(false);
2403
+ });
2404
+ return () => {
2405
+ controller.abort();
2406
+ };
2407
+ }, [activeCatalogKey, activeTab, catalogByLayerField, client.layers, extractCatalogValues, selectedFilterField, selectedFilterLayer]);
2408
+ const handleApplyFilter = import_react6.default.useCallback(async () => {
2409
+ if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
2410
+ setApplyingFilter(true);
2411
+ setFilterError(null);
2412
+ try {
2413
+ await onApplyLayerFilter({
2414
+ layerId: selectedFilterLayer.mapLayer.layerId,
2415
+ field: selectedFilterField,
2416
+ value: selectedFilterValue
2417
+ });
2418
+ setAppliedFilter({
2419
+ layerId: selectedFilterLayer.mapLayer.layerId,
2420
+ field: selectedFilterField,
2421
+ value: selectedFilterValue
2422
+ });
2423
+ } catch (error) {
2424
+ const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
2425
+ setFilterError(message);
2426
+ } finally {
2427
+ setApplyingFilter(false);
2428
+ }
2429
+ }, [onApplyLayerFilter, selectedFilterField, selectedFilterLayer, selectedFilterValue]);
2430
+ const handleClearFilter = import_react6.default.useCallback(async () => {
2431
+ if (!selectedFilterLayer) return;
2432
+ setApplyingFilter(true);
2433
+ setFilterError(null);
2434
+ try {
2435
+ await onClearLayerFilter?.({ layerId: selectedFilterLayer.mapLayer.layerId, field: selectedFilterField || void 0 });
2436
+ setSelectedFilterValue("");
2437
+ setAppliedFilter(null);
2438
+ } catch (error) {
2439
+ const message = error instanceof Error ? error.message : "No se pudo limpiar el filtro";
2440
+ setFilterError(message);
2441
+ } finally {
2442
+ setApplyingFilter(false);
2443
+ }
2444
+ }, [onClearLayerFilter, selectedFilterField, selectedFilterLayer]);
2205
2445
  const resolveLayerStyle = import_react6.default.useCallback(
2206
2446
  (layerId) => {
2207
2447
  const layerKey = String(layerId);
@@ -2540,6 +2780,18 @@ var ZenitLayerManager = ({
2540
2780
  "Subir"
2541
2781
  ]
2542
2782
  }
2783
+ ),
2784
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2785
+ "button",
2786
+ {
2787
+ type: "button",
2788
+ className: `zlm-tab${activeTab === "filters" ? " is-active" : ""}`,
2789
+ onClick: () => setActiveTab("filters"),
2790
+ children: [
2791
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Layers, { size: 16 }),
2792
+ "Filtros"
2793
+ ]
2794
+ }
2543
2795
  )
2544
2796
  ]
2545
2797
  }
@@ -2547,6 +2799,68 @@ var ZenitLayerManager = ({
2547
2799
  ] }),
2548
2800
  panelVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2549
2801
  activeTab === "layers" && renderLayerCards(),
2802
+ activeTab === "filters" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "zlm-filter-panel", style: { display: "flex", flexDirection: "column", gap: 10, background: "#fff", border: "1px solid #e2e8f0", borderRadius: 12, padding: 12 }, children: !filterableLayers.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2803
+ filterableLayers.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2804
+ "Capa",
2805
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { className: "zlm-filter-select", value: selectedFilterLayerId, onChange: (e) => setSelectedFilterLayerId(e.target.value), children: filterableLayers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: String(layer.mapLayer.layerId), children: layer.layerName ?? `Capa ${layer.mapLayer.layerId}` }, String(layer.mapLayer.layerId))) })
2806
+ ] }),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2808
+ "Campo",
2809
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { className: "zlm-filter-select", value: selectedFilterField, onChange: (e) => {
2810
+ setSelectedFilterField(e.target.value);
2811
+ setSelectedFilterValue("");
2812
+ }, children: filterFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: field, children: field }, field)) })
2813
+ ] }),
2814
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2815
+ "Valor",
2816
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2817
+ "select",
2818
+ {
2819
+ className: "zlm-filter-select",
2820
+ value: selectedFilterValue,
2821
+ onChange: (e) => {
2822
+ const value = e.target.value;
2823
+ setSelectedFilterValue(value);
2824
+ },
2825
+ children: [
2826
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "Seleccionar\u2026" }),
2827
+ activeCatalogValues.map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value, children: value }, value))
2828
+ ]
2829
+ }
2830
+ )
2831
+ ] }),
2832
+ loadingCatalog && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 12 }, children: "Cargando cat\xE1logo\u2026" }),
2833
+ !loadingCatalog && selectedFilterField && activeCatalogValues.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 12 }, children: "No hay cat\xE1logo disponible para este filtro." }),
2834
+ filterError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#b91c1c", fontSize: 12 }, children: filterError }),
2835
+ appliedFilter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-badge", style: { alignSelf: "flex-start" }, children: [
2836
+ "Activo: ",
2837
+ appliedFilter.field,
2838
+ " = ",
2839
+ appliedFilter.value
2840
+ ] }),
2841
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-filter-actions", style: { display: "flex", gap: 8 }, children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2843
+ "button",
2844
+ {
2845
+ type: "button",
2846
+ className: "zlm-panel-toggle",
2847
+ disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
2848
+ onClick: handleApplyFilter,
2849
+ children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
2850
+ }
2851
+ ),
2852
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2853
+ "button",
2854
+ {
2855
+ type: "button",
2856
+ className: "zlm-panel-toggle",
2857
+ disabled: applyingFilter,
2858
+ onClick: handleClearFilter,
2859
+ children: "Limpiar"
2860
+ }
2861
+ )
2862
+ ] })
2863
+ ] }) }),
2550
2864
  showUploadTab && activeTab === "upload" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2551
2865
  ] })
2552
2866
  ] });