zenit-sdk 0.1.1 → 0.1.3

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.
@@ -1,5 +1,5 @@
1
1
  // src/react/ZenitMap.tsx
2
- import React4, { useCallback as useCallback2, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo2, useState as useState2, forwardRef } from "react";
2
+ import React4, { useCallback as useCallback2, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo2, useRef as useRef5, useState as useState2, forwardRef } from "react";
3
3
  import { MapContainer, Marker as Marker2, TileLayer, ZoomControl } from "react-leaflet";
4
4
  import L4 from "leaflet";
5
5
 
@@ -251,6 +251,26 @@ import { GeoJSON } from "react-leaflet";
251
251
  import L from "leaflet";
252
252
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
253
253
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
254
+ function normalizeBboxFromData(data) {
255
+ const bboxCandidate = data.bbox;
256
+ if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
257
+ const [minLon, minLat, maxLon, maxLat] = bboxCandidate;
258
+ const values = [minLon, minLat, maxLon, maxLat].map((value) => Number(value));
259
+ if (values.every((value) => Number.isFinite(value))) {
260
+ return values;
261
+ }
262
+ return null;
263
+ }
264
+ function buildIdsSample(features) {
265
+ return features.slice(0, 10).map((feature) => {
266
+ const typedFeature = feature;
267
+ if (typedFeature.id !== void 0 && typedFeature.id !== null) {
268
+ return String(typedFeature.id);
269
+ }
270
+ const featureId = typedFeature.properties?.featureId;
271
+ return featureId !== void 0 && featureId !== null ? String(featureId) : "";
272
+ }).join(",");
273
+ }
254
274
  function getGeometryType(feature) {
255
275
  const t = feature?.geometry?.type;
256
276
  return typeof t === "string" ? t : null;
@@ -286,6 +306,18 @@ var LayerGeoJson = ({
286
306
  const features = data.features ?? [];
287
307
  const fillFeatures = features.filter(isNonPointGeometry);
288
308
  const pointFeatures = features.filter(isPointGeometry);
309
+ const dataVersionRef = useRef(0);
310
+ const prevSignatureRef = useRef("");
311
+ const firstId = features.length > 0 ? String(features[0]?.id ?? "") : "";
312
+ const lastId = features.length > 0 ? String(features[features.length - 1]?.id ?? "") : "";
313
+ const bbox = normalizeBboxFromData(data);
314
+ const idsSample = buildIdsSample(features);
315
+ const signature = bbox ? `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}|${bbox[0]}|${bbox[1]}|${bbox[2]}|${bbox[3]}` : `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}`;
316
+ const signatureToken = signature.replace(/[^a-zA-Z0-9_-]/g, "_");
317
+ if (prevSignatureRef.current !== signature) {
318
+ dataVersionRef.current += 1;
319
+ prevSignatureRef.current = signature;
320
+ }
289
321
  const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
290
322
  const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
291
323
  const clusterLayerRef = useRef(null);
@@ -338,7 +370,7 @@ var LayerGeoJson = ({
338
370
  onPolygonLabel?.(feature, layer);
339
371
  }
340
372
  },
341
- `fill-${layerId}`
373
+ `fill-${layerId}-${signatureToken}-v${dataVersionRef.current}`
342
374
  ),
343
375
  pointsData && !canCluster && /* @__PURE__ */ jsx(
344
376
  GeoJSON,
@@ -351,7 +383,7 @@ var LayerGeoJson = ({
351
383
  }),
352
384
  onEachFeature
353
385
  },
354
- `points-${layerId}`
386
+ `points-${layerId}-${signatureToken}-v${dataVersionRef.current}`
355
387
  )
356
388
  ] });
357
389
  };
@@ -911,6 +943,18 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
911
943
  // src/react/map/map-handlers.tsx
912
944
  import { useEffect as useEffect4, useRef as useRef4 } from "react";
913
945
  import { useMap as useMap2 } from "react-leaflet";
946
+ var MapInvalidator = ({ trigger }) => {
947
+ const map = useMap2();
948
+ const lastTrigger = useRef4(void 0);
949
+ useEffect4(() => {
950
+ if (lastTrigger.current === trigger) return;
951
+ lastTrigger.current = trigger;
952
+ requestAnimationFrame(() => {
953
+ map.invalidateSize();
954
+ });
955
+ }, [map, trigger]);
956
+ return null;
957
+ };
914
958
  function computeBBoxFromGeojson(geojson) {
915
959
  if (!geojson || !Array.isArray(geojson.features)) return null;
916
960
  const coords = [];
@@ -1139,6 +1183,8 @@ var ZenitMap = forwardRef(({
1139
1183
  const [loadingMap, setLoadingMap] = useState2(false);
1140
1184
  const [mapError, setMapError] = useState2(null);
1141
1185
  const [mapInstance, setMapInstance] = useState2(null);
1186
+ const [layerGeojsonOverrides, setLayerGeojsonOverrides] = useState2({});
1187
+ const originalGeojsonByLayerIdRef = useRef5({});
1142
1188
  const [panesReady, setPanesReady] = useState2(false);
1143
1189
  const [currentZoom, setCurrentZoom] = useState2(initialZoom ?? DEFAULT_ZOOM);
1144
1190
  const [isPopupOpen, setIsPopupOpen] = useState2(false);
@@ -1424,13 +1470,27 @@ var ZenitMap = forwardRef(({
1424
1470
  useEffect5(() => {
1425
1471
  setCurrentZoom(zoom);
1426
1472
  }, [zoom]);
1473
+ useEffect5(() => {
1474
+ if (!layerGeojson) return;
1475
+ layers.forEach((layer) => {
1476
+ const layerKey = String(layer.mapLayer.layerId);
1477
+ const incoming = layerGeojson[layer.mapLayer.layerId] ?? layerGeojson[layerKey] ?? null;
1478
+ if (incoming && !originalGeojsonByLayerIdRef.current[layerKey]) {
1479
+ originalGeojsonByLayerIdRef.current[layerKey] = incoming;
1480
+ }
1481
+ });
1482
+ }, [layerGeojson, layers]);
1427
1483
  const decoratedLayers = useMemo2(() => {
1428
- return layers.map((layer) => ({
1429
- ...layer,
1430
- effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1431
- data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1432
- }));
1433
- }, [effectiveStates, layerGeojson, layers]);
1484
+ return layers.map((layer) => {
1485
+ const layerKey = String(layer.mapLayer.layerId);
1486
+ const override = layerGeojsonOverrides[layerKey];
1487
+ return {
1488
+ ...layer,
1489
+ effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1490
+ data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
1491
+ };
1492
+ });
1493
+ }, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
1434
1494
  const orderedLayers = useMemo2(() => {
1435
1495
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1436
1496
  }, [decoratedLayers]);
@@ -1659,6 +1719,24 @@ var ZenitMap = forwardRef(({
1659
1719
  };
1660
1720
  mapInstance.fitBounds(bounds, fitOptions);
1661
1721
  },
1722
+ fitToBbox: (bbox, padding) => {
1723
+ if (!mapInstance) return;
1724
+ 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)) {
1725
+ console.warn("[ZenitMap.fitToBbox] Invalid bbox", bbox);
1726
+ return;
1727
+ }
1728
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1729
+ const bounds = L4.latLngBounds([bbox.minLat, bbox.minLon], [bbox.maxLat, bbox.maxLon]);
1730
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1731
+ },
1732
+ fitToGeoJson: (fc, padding) => {
1733
+ if (!mapInstance) return;
1734
+ if (!fc?.features?.length) return;
1735
+ const bounds = L4.geoJSON(fc).getBounds();
1736
+ if (!bounds.isValid()) return;
1737
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1738
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1739
+ },
1662
1740
  setView: (coordinates, zoom2) => {
1663
1741
  if (!mapInstance) return;
1664
1742
  mapInstance.setView([coordinates.lat, coordinates.lon], zoom2 ?? mapInstance.getZoom(), {
@@ -1683,8 +1761,25 @@ var ZenitMap = forwardRef(({
1683
1761
  highlightFeature: (layerId, featureId) => {
1684
1762
  upsertUiOverride(layerId, { overrideVisible: true, overrideOpacity: 1 });
1685
1763
  },
1764
+ updateLayerGeoJson: (layerId, featureCollection) => {
1765
+ const layerKey = String(layerId);
1766
+ setLayerGeojsonOverrides((prev) => ({ ...prev, [layerKey]: featureCollection }));
1767
+ },
1768
+ restoreLayerGeoJson: (layerId) => {
1769
+ const layerKey = String(layerId);
1770
+ const original = originalGeojsonByLayerIdRef.current[layerKey];
1771
+ setLayerGeojsonOverrides((prev) => {
1772
+ const next = { ...prev };
1773
+ if (original) {
1774
+ next[layerKey] = original;
1775
+ } else {
1776
+ delete next[layerKey];
1777
+ }
1778
+ return next;
1779
+ });
1780
+ },
1686
1781
  getMapInstance: () => mapInstance
1687
- }), [effectiveStates, mapInstance]);
1782
+ }), [effectiveStates, isMobile, mapInstance]);
1688
1783
  if (loadingMap) {
1689
1784
  return /* @__PURE__ */ jsx3("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1690
1785
  }
@@ -1737,6 +1832,7 @@ var ZenitMap = forwardRef(({
1737
1832
  ),
1738
1833
  /* @__PURE__ */ jsx3(ZoomControl, { position: "topright" }),
1739
1834
  /* @__PURE__ */ jsx3(MapInstanceBridge, { onReady: handleMapReady }),
1835
+ /* @__PURE__ */ jsx3(MapInvalidator, { trigger: mapId }),
1740
1836
  /* @__PURE__ */ jsx3(
1741
1837
  BBoxZoomHandler,
1742
1838
  {
@@ -1916,7 +2012,7 @@ import React5, { useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6
1916
2012
  import { Eye, EyeOff, ChevronLeft, ChevronRight, Layers, Upload, X, ZoomIn } from "lucide-react";
1917
2013
 
1918
2014
  // src/react/ZenitLayerManager.tsx
1919
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2015
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1920
2016
  var FLOAT_TOLERANCE = 1e-3;
1921
2017
  function areEffectiveStatesEqual(a, b) {
1922
2018
  if (a.length !== b.length) return false;
@@ -1954,7 +2050,9 @@ var ZenitLayerManager = ({
1954
2050
  showUploadTab = true,
1955
2051
  showLayerVisibilityIcon = true,
1956
2052
  layerFeatureCounts,
1957
- mapLayers
2053
+ mapLayers,
2054
+ onApplyLayerFilter,
2055
+ onClearLayerFilter
1958
2056
  }) => {
1959
2057
  const [map, setMap] = useState3(null);
1960
2058
  const [loadingMap, setLoadingMap] = useState3(false);
@@ -1962,6 +2060,15 @@ var ZenitLayerManager = ({
1962
2060
  const [layers, setLayers] = useState3([]);
1963
2061
  const [activeTab, setActiveTab] = useState3("layers");
1964
2062
  const [panelVisible, setPanelVisible] = useState3(true);
2063
+ const [selectedFilterLayerId, setSelectedFilterLayerId] = useState3("");
2064
+ const [selectedFilterField, setSelectedFilterField] = useState3("");
2065
+ const [selectedFilterValue, setSelectedFilterValue] = useState3("");
2066
+ const [catalogByLayerField, setCatalogByLayerField] = useState3({});
2067
+ const [loadingCatalog, setLoadingCatalog] = useState3(false);
2068
+ const [applyingFilter, setApplyingFilter] = useState3(false);
2069
+ const [filterError, setFilterError] = useState3(null);
2070
+ const [appliedFilter, setAppliedFilter] = useState3(null);
2071
+ const catalogAbortRef = useRef6(null);
1965
2072
  const lastEmittedStatesRef = useRef6(null);
1966
2073
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
1967
2074
  const baseStates = useMemo3(
@@ -2155,6 +2262,139 @@ var ZenitLayerManager = ({
2155
2262
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
2156
2263
  });
2157
2264
  }, [effectiveStates, layers, resolveFeatureCount]);
2265
+ const filterableLayers = useMemo3(() => {
2266
+ return decoratedLayers.filter((entry) => {
2267
+ const prefilters = entry.mapLayer.layerConfig?.prefilters;
2268
+ return !!prefilters && Object.keys(prefilters).length > 0;
2269
+ });
2270
+ }, [decoratedLayers]);
2271
+ const selectedFilterLayer = useMemo3(
2272
+ () => filterableLayers.find((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId) ?? null,
2273
+ [filterableLayers, selectedFilterLayerId]
2274
+ );
2275
+ const filterFields = useMemo3(() => {
2276
+ const prefilters = selectedFilterLayer?.mapLayer.layerConfig?.prefilters;
2277
+ return prefilters ? Object.keys(prefilters) : [];
2278
+ }, [selectedFilterLayer]);
2279
+ const activeCatalogKey = selectedFilterLayer ? `${selectedFilterLayer.mapLayer.layerId}:${selectedFilterField}` : null;
2280
+ const activeCatalogValues = activeCatalogKey ? catalogByLayerField[activeCatalogKey] ?? [] : [];
2281
+ const extractCatalogValues = React5.useCallback((catalogData, field) => {
2282
+ const values = /* @__PURE__ */ new Set();
2283
+ const pushValue = (value) => {
2284
+ if (value === null || value === void 0) return;
2285
+ const normalized = String(value).trim();
2286
+ if (normalized) values.add(normalized);
2287
+ };
2288
+ if (catalogData && typeof catalogData === "object") {
2289
+ const maybeRecord = catalogData;
2290
+ const directField = maybeRecord[field];
2291
+ if (Array.isArray(directField)) {
2292
+ directField.forEach(pushValue);
2293
+ }
2294
+ const items = maybeRecord.items;
2295
+ if (Array.isArray(items)) {
2296
+ items.forEach((item) => {
2297
+ if (!item || typeof item !== "object") return;
2298
+ const row = item;
2299
+ const rowField = row.field;
2300
+ if (String(rowField ?? "").toUpperCase() === field.toUpperCase() && Array.isArray(row.values)) {
2301
+ row.values.forEach(pushValue);
2302
+ }
2303
+ });
2304
+ }
2305
+ const features = maybeRecord.features;
2306
+ if (Array.isArray(features)) {
2307
+ features.forEach((feature) => {
2308
+ if (!feature || typeof feature !== "object") return;
2309
+ const properties = feature.properties;
2310
+ if (properties && field in properties) {
2311
+ pushValue(properties[field]);
2312
+ }
2313
+ });
2314
+ }
2315
+ }
2316
+ return [...values].sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
2317
+ }, []);
2318
+ useEffect6(() => {
2319
+ if (!filterableLayers.length) {
2320
+ setSelectedFilterLayerId("");
2321
+ return;
2322
+ }
2323
+ if (!selectedFilterLayerId || !filterableLayers.some((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId)) {
2324
+ setSelectedFilterLayerId(String(filterableLayers[0].mapLayer.layerId));
2325
+ }
2326
+ }, [filterableLayers, selectedFilterLayerId]);
2327
+ useEffect6(() => {
2328
+ if (!filterFields.length) {
2329
+ setSelectedFilterField("");
2330
+ return;
2331
+ }
2332
+ if (!selectedFilterField || !filterFields.includes(selectedFilterField)) {
2333
+ setSelectedFilterField(filterFields[0]);
2334
+ setSelectedFilterValue("");
2335
+ }
2336
+ }, [filterFields, selectedFilterField]);
2337
+ useEffect6(() => {
2338
+ if (activeTab !== "filters") return;
2339
+ if (!selectedFilterLayer || !selectedFilterField || !activeCatalogKey) return;
2340
+ if (catalogByLayerField[activeCatalogKey]) return;
2341
+ catalogAbortRef.current?.abort();
2342
+ const controller = new AbortController();
2343
+ catalogAbortRef.current = controller;
2344
+ setLoadingCatalog(true);
2345
+ setFilterError(null);
2346
+ client.layers.getLayerFeaturesCatalog(selectedFilterLayer.mapLayer.layerId).then((response) => {
2347
+ if (controller.signal.aborted) return;
2348
+ const values = extractCatalogValues(response.data, selectedFilterField);
2349
+ setCatalogByLayerField((prev) => ({ ...prev, [activeCatalogKey]: values }));
2350
+ }).catch((error) => {
2351
+ if (controller.signal.aborted) return;
2352
+ const message = error instanceof Error ? error.message : "No se pudo cargar el cat\xE1logo";
2353
+ setFilterError(message);
2354
+ }).finally(() => {
2355
+ if (!controller.signal.aborted) setLoadingCatalog(false);
2356
+ });
2357
+ return () => {
2358
+ controller.abort();
2359
+ };
2360
+ }, [activeCatalogKey, activeTab, catalogByLayerField, client.layers, extractCatalogValues, selectedFilterField, selectedFilterLayer]);
2361
+ const handleApplyFilter = React5.useCallback(async () => {
2362
+ if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
2363
+ setApplyingFilter(true);
2364
+ setFilterError(null);
2365
+ try {
2366
+ await onApplyLayerFilter({
2367
+ layerId: selectedFilterLayer.mapLayer.layerId,
2368
+ field: selectedFilterField,
2369
+ value: selectedFilterValue
2370
+ });
2371
+ setAppliedFilter({
2372
+ layerId: selectedFilterLayer.mapLayer.layerId,
2373
+ field: selectedFilterField,
2374
+ value: selectedFilterValue
2375
+ });
2376
+ } catch (error) {
2377
+ const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
2378
+ setFilterError(message);
2379
+ } finally {
2380
+ setApplyingFilter(false);
2381
+ }
2382
+ }, [onApplyLayerFilter, selectedFilterField, selectedFilterLayer, selectedFilterValue]);
2383
+ const handleClearFilter = React5.useCallback(async () => {
2384
+ if (!selectedFilterLayer) return;
2385
+ setApplyingFilter(true);
2386
+ setFilterError(null);
2387
+ try {
2388
+ await onClearLayerFilter?.({ layerId: selectedFilterLayer.mapLayer.layerId, field: selectedFilterField || void 0 });
2389
+ setSelectedFilterValue("");
2390
+ setAppliedFilter(null);
2391
+ } catch (error) {
2392
+ const message = error instanceof Error ? error.message : "No se pudo limpiar el filtro";
2393
+ setFilterError(message);
2394
+ } finally {
2395
+ setApplyingFilter(false);
2396
+ }
2397
+ }, [onClearLayerFilter, selectedFilterField, selectedFilterLayer]);
2158
2398
  const resolveLayerStyle = React5.useCallback(
2159
2399
  (layerId) => {
2160
2400
  const layerKey = String(layerId);
@@ -2493,6 +2733,18 @@ var ZenitLayerManager = ({
2493
2733
  "Subir"
2494
2734
  ]
2495
2735
  }
2736
+ ),
2737
+ /* @__PURE__ */ jsxs4(
2738
+ "button",
2739
+ {
2740
+ type: "button",
2741
+ className: `zlm-tab${activeTab === "filters" ? " is-active" : ""}`,
2742
+ onClick: () => setActiveTab("filters"),
2743
+ children: [
2744
+ /* @__PURE__ */ jsx4(Layers, { size: 16 }),
2745
+ "Filtros"
2746
+ ]
2747
+ }
2496
2748
  )
2497
2749
  ]
2498
2750
  }
@@ -2500,6 +2752,68 @@ var ZenitLayerManager = ({
2500
2752
  ] }),
2501
2753
  panelVisible && /* @__PURE__ */ jsxs4("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2502
2754
  activeTab === "layers" && renderLayerCards(),
2755
+ activeTab === "filters" && /* @__PURE__ */ jsx4("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__ */ jsx4("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
2756
+ filterableLayers.length > 1 && /* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2757
+ "Capa",
2758
+ /* @__PURE__ */ jsx4("select", { className: "zlm-filter-select", value: selectedFilterLayerId, onChange: (e) => setSelectedFilterLayerId(e.target.value), children: filterableLayers.map((layer) => /* @__PURE__ */ jsx4("option", { value: String(layer.mapLayer.layerId), children: layer.layerName ?? `Capa ${layer.mapLayer.layerId}` }, String(layer.mapLayer.layerId))) })
2759
+ ] }),
2760
+ /* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2761
+ "Campo",
2762
+ /* @__PURE__ */ jsx4("select", { className: "zlm-filter-select", value: selectedFilterField, onChange: (e) => {
2763
+ setSelectedFilterField(e.target.value);
2764
+ setSelectedFilterValue("");
2765
+ }, children: filterFields.map((field) => /* @__PURE__ */ jsx4("option", { value: field, children: field }, field)) })
2766
+ ] }),
2767
+ /* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2768
+ "Valor",
2769
+ /* @__PURE__ */ jsxs4(
2770
+ "select",
2771
+ {
2772
+ className: "zlm-filter-select",
2773
+ value: selectedFilterValue,
2774
+ onChange: (e) => {
2775
+ const value = e.target.value;
2776
+ setSelectedFilterValue(value);
2777
+ },
2778
+ children: [
2779
+ /* @__PURE__ */ jsx4("option", { value: "", children: "Seleccionar\u2026" }),
2780
+ activeCatalogValues.map((value) => /* @__PURE__ */ jsx4("option", { value, children: value }, value))
2781
+ ]
2782
+ }
2783
+ )
2784
+ ] }),
2785
+ loadingCatalog && /* @__PURE__ */ jsx4("div", { style: { color: "#64748b", fontSize: 12 }, children: "Cargando cat\xE1logo\u2026" }),
2786
+ !loadingCatalog && selectedFilterField && activeCatalogValues.length === 0 && /* @__PURE__ */ jsx4("div", { style: { color: "#64748b", fontSize: 12 }, children: "No hay cat\xE1logo disponible para este filtro." }),
2787
+ filterError && /* @__PURE__ */ jsx4("div", { style: { color: "#b91c1c", fontSize: 12 }, children: filterError }),
2788
+ appliedFilter && /* @__PURE__ */ jsxs4("div", { className: "zlm-badge", style: { alignSelf: "flex-start" }, children: [
2789
+ "Activo: ",
2790
+ appliedFilter.field,
2791
+ " = ",
2792
+ appliedFilter.value
2793
+ ] }),
2794
+ /* @__PURE__ */ jsxs4("div", { className: "zlm-filter-actions", style: { display: "flex", gap: 8 }, children: [
2795
+ /* @__PURE__ */ jsx4(
2796
+ "button",
2797
+ {
2798
+ type: "button",
2799
+ className: "zlm-panel-toggle",
2800
+ disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
2801
+ onClick: handleApplyFilter,
2802
+ children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
2803
+ }
2804
+ ),
2805
+ /* @__PURE__ */ jsx4(
2806
+ "button",
2807
+ {
2808
+ type: "button",
2809
+ className: "zlm-panel-toggle",
2810
+ disabled: applyingFilter,
2811
+ onClick: handleClearFilter,
2812
+ children: "Limpiar"
2813
+ }
2814
+ )
2815
+ ] })
2816
+ ] }) }),
2503
2817
  showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx4("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2504
2818
  ] })
2505
2819
  ] });
@@ -2914,7 +3228,7 @@ var MarkdownRenderer = ({ content, className }) => {
2914
3228
  };
2915
3229
 
2916
3230
  // src/react/ai/FloatingChatBox.tsx
2917
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3231
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2918
3232
  var ChatIcon = () => /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2919
3233
  var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2920
3234
  /* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
@@ -3642,7 +3956,7 @@ var FloatingChatBox = ({
3642
3956
  ...styles.messageBubble,
3643
3957
  ...styles.assistantMessage
3644
3958
  },
3645
- children: streamingText ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
3959
+ children: streamingText ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
3646
3960
  /* @__PURE__ */ jsx7(MarkdownRenderer, { content: streamingText }),
3647
3961
  /* @__PURE__ */ jsx7("span", { style: styles.cursor })
3648
3962
  ] }) : /* @__PURE__ */ jsxs6("div", { style: styles.thinkingText, children: [
@@ -3709,7 +4023,7 @@ var FloatingChatBox = ({
3709
4023
  },
3710
4024
  onClick: () => setOpen((prev) => !prev),
3711
4025
  "aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
3712
- children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
4026
+ children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
3713
4027
  /* @__PURE__ */ jsx7(ChatIcon, {}),
3714
4028
  !isMobile && /* @__PURE__ */ jsx7("span", { children: "Asistente IA" })
3715
4029
  ] })
@@ -3760,4 +4074,4 @@ export {
3760
4074
  useSendMessageStream,
3761
4075
  FloatingChatBox
3762
4076
  };
3763
- //# sourceMappingURL=chunk-OOK4DBG5.mjs.map
4077
+ //# sourceMappingURL=chunk-URDEEWUZ.mjs.map