@opengeoweb/time-series 15.3.0 → 16.0.0

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.
package/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import React, { useCallback, useState, useMemo, useEffect, memo } from 'react';
2
+ import React, { useCallback, useMemo, useState, useEffect, memo } from 'react';
3
3
  import { useDispatch, useSelector } from 'react-redux';
4
4
  import { CustomIconButton, dateUtils, TooltipSelect, CustomAccordion, CustomTooltip, CustomToggleButton, ToolContainerDraggable, ToggleMenu, CustomDialog, SearchField, SearchHighlight, AlertBanner, useConfirmationDialog, SwitchButton, useResizeObserver } from '@opengeoweb/shared';
5
5
  import { snackbarActions, snackbarTypes, snackbarListener, snackbarReducer } from '@opengeoweb/snackbar';
@@ -17,7 +17,7 @@ import { v4 } from 'uuid';
17
17
  import { center } from '@turf/turf';
18
18
  import { MapFeatureClass } from '@opengeoweb/webmap-react';
19
19
  import { useQueries, useQueryClient, useQuery, queryOptions } from '@tanstack/react-query';
20
- import { handleResponse } from '@opengeoweb/api';
20
+ import { handleResponse, withJsonFormat } from '@opengeoweb/api';
21
21
  import Box$1 from '@mui/material/Box';
22
22
  import { useFormContext } from 'react-hook-form';
23
23
  import { ReactHookFormProvider, defaultFormOptions, ReactHookFormTextField, ReactHookFormSelect } from '@opengeoweb/form-fields';
@@ -138,8 +138,10 @@ var en = {
138
138
  "timeseries-legend": "Legend",
139
139
  "timeseries-legend-tooltip-show": "Show legend",
140
140
  "timeseries-legend-tooltip-hide": "Hide legend",
141
- "error-title": "Some parameters could not be loaded.",
141
+ "error-message-general": "This parameter could not be loaded.",
142
+ "error-message-general-plural": "These parameters could not be loaded.",
142
143
  "error-message-missing-collection-or-service": "No collection and/or service found for parameter.",
144
+ "all-plots": "All plots",
143
145
  "timeseries-add-plot": "Add plot"
144
146
  };
145
147
  var fi = {
@@ -250,8 +252,10 @@ var fi = {
250
252
  "timeseries-legend": "Selite",
251
253
  "timeseries-legend-tooltip-show": "Näytä selite",
252
254
  "timeseries-legend-tooltip-hide": "Piilota selite",
253
- "error-title": "Joitain parametreja ei pystytty lataamaan",
255
+ "error-message-general": "Tätä parametria ei voitu ladata.",
256
+ "error-message-general-plural": "Näitä parametreja ei voitu ladata.",
254
257
  "error-message-missing-collection-or-service": "Parametrille ei löytynyt kokoelmaa ja/tai palvelua",
258
+ "all-plots": "Kaikki kaaviot",
255
259
  "timeseries-add-plot": "Lisää kaavio"
256
260
  };
257
261
  var no = {
@@ -362,8 +366,10 @@ var no = {
362
366
  "timeseries-legend": "Forklaring",
363
367
  "timeseries-legend-tooltip-show": "Vis forklaring",
364
368
  "timeseries-legend-tooltip-hide": "Skjul forklaring",
365
- "error-title": "Noen parametere kunne ikke lastes.",
369
+ "error-message-general": "Denne parameteren kunne ikke lastes.",
370
+ "error-message-general-plural": "Disse parametrene kunne ikke lastes.",
366
371
  "error-message-missing-collection-or-service": "Ingen kolleksjon og/eller tjeneste funnet for parameter.",
372
+ "all-plots": "Alle diagrammer",
367
373
  "timeseries-add-plot": "Legg til plott"
368
374
  };
369
375
  var nl = {
@@ -479,8 +485,10 @@ var nl = {
479
485
  "timeseries-legend": "Legenda",
480
486
  "timeseries-legend-tooltip-show": "Toon legenda",
481
487
  "timeseries-legend-tooltip-hide": "Verberg legenda",
482
- "error-title": "Sommige parameters konden niet worden geladen.",
488
+ "error-message-general": "Deze parameter kon niet worden geladen.",
489
+ "error-message-general-plural": "Deze parameters konden niet worden geladen.",
483
490
  "error-message-missing-collection-or-service": "Geen collectie en/of service gevonden voor parameter.",
491
+ "all-plots": "Alle grafieken",
484
492
  "timeseries-add-plot": "Grafiek toevoegen"
485
493
  };
486
494
  var timeseriesTranslations = {
@@ -2707,13 +2715,15 @@ var getNearbyLocation = function getNearbyLocation(parameter, service, selectedL
2707
2715
  }
2708
2716
  return null;
2709
2717
  };
2710
- var getPlotsWithParameters = function getPlotsWithParameters(plots, parameters) {
2718
+ var getPlotsWithParameters = function getPlotsWithParameters(plots, parameters, plotErrors, failedParameters) {
2711
2719
  var parametersByPlotId = groupBy(parameters, function (parameter) {
2712
2720
  return parameter.plotId;
2713
2721
  });
2714
2722
  var plotsWithParameters = plots.map(function (plot) {
2715
2723
  return Object.assign({}, plot, {
2716
- parametersWithData: parametersByPlotId[plot.plotId]
2724
+ parametersWithData: parametersByPlotId[plot.plotId] || [],
2725
+ errors: plotErrors == null ? void 0 : plotErrors[plot.plotId],
2726
+ failedParameters: failedParameters == null ? void 0 : failedParameters[plot.plotId]
2717
2727
  });
2718
2728
  }).filter(function (plot) {
2719
2729
  var _plot$parametersWithD;
@@ -2790,7 +2800,7 @@ var getLocations = /*#__PURE__*/function () {
2790
2800
  return _regeneratorRuntime().wrap(function _callee$(_context) {
2791
2801
  while (1) switch (_context.prev = _context.next) {
2792
2802
  case 0:
2793
- url = createUrl(serviceUrl, "/collections/" + collectionId + "/locations");
2803
+ url = withJsonFormat(createUrl(serviceUrl, "/collections/" + collectionId + "/locations"));
2794
2804
  return _context.abrupt("return", fetch(url).then(handleResponse));
2795
2805
  case 2:
2796
2806
  case "end":
@@ -2808,7 +2818,7 @@ var getServiceInformation = /*#__PURE__*/function () {
2808
2818
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
2809
2819
  while (1) switch (_context2.prev = _context2.next) {
2810
2820
  case 0:
2811
- url = createUrl(serviceUrl);
2821
+ url = withJsonFormat(createUrl(serviceUrl));
2812
2822
  return _context2.abrupt("return", fetch(url).then(handleResponse));
2813
2823
  case 2:
2814
2824
  case "end":
@@ -2826,7 +2836,7 @@ var getCollections = /*#__PURE__*/function () {
2826
2836
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
2827
2837
  while (1) switch (_context3.prev = _context3.next) {
2828
2838
  case 0:
2829
- url = createUrl(serviceUrl, '/collections');
2839
+ url = withJsonFormat(createUrl(serviceUrl, '/collections'));
2830
2840
  return _context3.abrupt("return", fetch(url).then(handleResponse));
2831
2841
  case 2:
2832
2842
  case "end":
@@ -2844,7 +2854,7 @@ var getCollection = /*#__PURE__*/function () {
2844
2854
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
2845
2855
  while (1) switch (_context4.prev = _context4.next) {
2846
2856
  case 0:
2847
- url = createUrl(serviceUrl, "/collections/" + collectionId);
2857
+ url = withJsonFormat(createUrl(serviceUrl, "/collections/" + collectionId));
2848
2858
  return _context4.abrupt("return", fetch(url).then(handleResponse));
2849
2859
  case 2:
2850
2860
  case "end":
@@ -2862,7 +2872,7 @@ var getInstances = /*#__PURE__*/function () {
2862
2872
  return _regeneratorRuntime().wrap(function _callee5$(_context5) {
2863
2873
  while (1) switch (_context5.prev = _context5.next) {
2864
2874
  case 0:
2865
- url = createUrl(serviceUrl, "/collections/" + collectionId + "/instances");
2875
+ url = withJsonFormat(createUrl(serviceUrl, "/collections/" + collectionId + "/instances"));
2866
2876
  return _context5.abrupt("return", fetch(url).then(handleResponse));
2867
2877
  case 2:
2868
2878
  case "end":
@@ -2913,8 +2923,8 @@ var getPosition = /*#__PURE__*/function () {
2913
2923
  return _ref7.apply(this, arguments);
2914
2924
  };
2915
2925
  }();
2916
- var getErrorMessage = function getErrorMessage(parameter) {
2917
- return parameter.propertyName + " - " + parameter.collectionId;
2926
+ var getErrorMessage = function getErrorMessage() {
2927
+ return 'error-message-general';
2918
2928
  };
2919
2929
  var fetchEdrParameter = /*#__PURE__*/function () {
2920
2930
  var _ref8 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee8(parameter, location, service, geojsonMap, intervalParam, collection, instance) {
@@ -2946,7 +2956,7 @@ var fetchEdrParameter = /*#__PURE__*/function () {
2946
2956
  case 11:
2947
2957
  _context8.prev = 11;
2948
2958
  _context8.t0 = _context8["catch"](5);
2949
- throw new Error(getErrorMessage(parameter));
2959
+ throw new Error(getErrorMessage());
2950
2960
  case 14:
2951
2961
  _context8.prev = 14;
2952
2962
  if (!((_instance$data_querie2 = instance.data_queries) != null && _instance$data_querie2.position || (_collection$dataQueri2 = collection.dataQueries) != null && _collection$dataQueri2.position)) {
@@ -2980,11 +2990,11 @@ var fetchEdrParameter = /*#__PURE__*/function () {
2980
2990
  case 31:
2981
2991
  _context8.prev = 31;
2982
2992
  _context8.t2 = _context8["catch"](25);
2983
- throw new Error(getErrorMessage(parameter));
2993
+ throw new Error(getErrorMessage());
2984
2994
  case 34:
2985
- throw new Error(getErrorMessage(parameter));
2995
+ throw new Error(getErrorMessage());
2986
2996
  case 35:
2987
- throw new Error(getErrorMessage(parameter));
2997
+ throw new Error(getErrorMessage());
2988
2998
  case 36:
2989
2999
  case "end":
2990
3000
  return _context8.stop();
@@ -3255,9 +3265,6 @@ var useGetCollectionsAndInstanceDetails = function useGetCollectionsAndInstanceD
3255
3265
  });
3256
3266
  };
3257
3267
  var useGetPlotsWithData = function useGetPlotsWithData(plotsWithoutData, selectedLocation, services, splitGeoJsonByCollection, t, syncgroupTimeState) {
3258
- var _useState = useState([]),
3259
- plotsWithData = _useState[0],
3260
- setPlotsWithData = _useState[1];
3261
3268
  // Create array of collections we need to query for the data
3262
3269
  var collectionsList = useMemo(function () {
3263
3270
  return extractUsedCollections(plotsWithoutData, services);
@@ -3289,7 +3296,7 @@ var useGetPlotsWithData = function useGetPlotsWithData(plotsWithoutData, selecte
3289
3296
  // Create here so if new data is advertised by the collection/instance a new request is made
3290
3297
  var intervalParam = constructTimeInterval(collection, instanceInfo, syncgroupTimeState);
3291
3298
  return {
3292
- queryKey: generateTimeSeriesQueryKeys(['get-parameter-data', parameter.propertyName, parameter.serviceId, parameter.collectionId, parameter.instanceId || 'undefined', ((_parameter$dimension = parameter.dimension) == null ? void 0 : _parameter$dimension.value) || 'undefined', intervalParam, selectedLocation.lat.toString() || 'undefined', selectedLocation.lon.toString() || 'undefined']),
3299
+ queryKey: generateTimeSeriesQueryKeys(['get-parameter-data', parameter.plotId, parameter.propertyName, parameter.serviceId, parameter.collectionId, parameter.instanceId || 'undefined', ((_parameter$dimension = parameter.dimension) == null ? void 0 : _parameter$dimension.value) || 'undefined', intervalParam, selectedLocation.lat.toString() || 'undefined', selectedLocation.lon.toString() || 'undefined']),
3293
3300
  queryFn: function () {
3294
3301
  var _queryFn7 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
3295
3302
  var edrResponse;
@@ -3338,9 +3345,16 @@ var useGetPlotsWithData = function useGetPlotsWithData(plotsWithoutData, selecte
3338
3345
  var isFetching = results.some(function (result) {
3339
3346
  return result.isFetching;
3340
3347
  });
3341
- var errorsWithCount = countBy(results.map(function (result) {
3348
+ var parameterErrors = {};
3349
+ results.forEach(function (result, index) {
3342
3350
  var _result$error;
3343
- return result == null || (_result$error = result.error) == null ? void 0 : _result$error.message;
3351
+ if ((_result$error = result.error) != null && _result$error.message) {
3352
+ parameterErrors[index] = result.error.message;
3353
+ }
3354
+ });
3355
+ var errorsWithCount = countBy(results.map(function (result) {
3356
+ var _result$error2;
3357
+ return result == null || (_result$error2 = result.error) == null ? void 0 : _result$error2.message;
3344
3358
  }).filter(Boolean));
3345
3359
  var errorMessage = Object.entries(errorsWithCount).map(function (_ref) {
3346
3360
  var message = _ref[0],
@@ -3351,17 +3365,38 @@ var useGetPlotsWithData = function useGetPlotsWithData(plotsWithoutData, selecte
3351
3365
  return {
3352
3366
  data: data,
3353
3367
  isFetching: isFetching,
3354
- error: error
3368
+ error: error,
3369
+ parameterErrors: parameterErrors
3355
3370
  };
3356
3371
  }
3357
3372
  });
3358
- // Combine fetched data with parameters and plots
3359
- useEffect(function () {
3373
+ // Combine fetched data with parameters and plots, including errors per plot
3374
+ var plotsWithData = useMemo(function () {
3360
3375
  var _results$data;
3361
3376
  var data = (_results$data = results.data) != null && _results$data.length ? results.data : [];
3362
- var result = getPlotsWithParameters(plotsWithoutData.plots, combineParametersWithData(plotsWithoutData.parameters, data));
3363
- setPlotsWithData(result);
3364
- }, [plotsWithoutData.parameters, plotsWithoutData.plots, results.data]);
3377
+ // Create maps of parameter errors and failed parameter names by plotId
3378
+ var plotErrors = {};
3379
+ var failedParametersByPlotId = {};
3380
+ plotsWithoutData.parameters.forEach(function (parameter, index) {
3381
+ var _results$parameterErr;
3382
+ var errorMessage = (_results$parameterErr = results.parameterErrors) == null ? void 0 : _results$parameterErr[index];
3383
+ if (errorMessage) {
3384
+ if (!plotErrors[parameter.plotId]) {
3385
+ plotErrors[parameter.plotId] = [];
3386
+ failedParametersByPlotId[parameter.plotId] = [];
3387
+ }
3388
+ if (!plotErrors[parameter.plotId].includes(errorMessage)) {
3389
+ plotErrors[parameter.plotId].push(errorMessage);
3390
+ }
3391
+ failedParametersByPlotId[parameter.plotId].push({
3392
+ propertyName: parameter.propertyName,
3393
+ serviceId: parameter.serviceId,
3394
+ collectionId: parameter.collectionId
3395
+ });
3396
+ }
3397
+ });
3398
+ return getPlotsWithParameters(plotsWithoutData.plots, combineParametersWithData(plotsWithoutData.parameters, data), plotErrors, failedParametersByPlotId);
3399
+ }, [plotsWithoutData.parameters, plotsWithoutData.plots, results.data, results.parameterErrors]);
3365
3400
  return Object.assign({}, results, {
3366
3401
  data: plotsWithData
3367
3402
  });
@@ -7601,7 +7636,8 @@ var CalculateTimeSpan = function CalculateTimeSpan(plotsWithData) {
7601
7636
  var useChartHeight = function useChartHeight(_ref) {
7602
7637
  var plotsWithData = _ref.plotsWithData,
7603
7638
  shouldShowLegend = _ref.shouldShowLegend,
7604
- option = _ref.option;
7639
+ option = _ref.option,
7640
+ onLegendHeightChange = _ref.onLegendHeightChange;
7605
7641
  var getDefaultHeight = function getDefaultHeight(plotsWithData) {
7606
7642
  return 100 + PLOT_HEIGHT * plotsWithData.length;
7607
7643
  };
@@ -7627,6 +7663,7 @@ var useChartHeight = function useChartHeight(_ref) {
7627
7663
  var legendView = echartsInstance.getViewOfComponentModel(legend);
7628
7664
  var _legendView$group$get = legendView.group.getBoundingRect(),
7629
7665
  height = _legendView$group$get.height;
7666
+ onLegendHeightChange == null || onLegendHeightChange(shouldShowLegend ? height : 0);
7630
7667
  var grid = (_option$grid = option.grid) != null ? _option$grid : [];
7631
7668
  var gridArray = Array.isArray(grid) ? grid : [];
7632
7669
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
@@ -7646,7 +7683,7 @@ var useChartHeight = function useChartHeight(_ref) {
7646
7683
  var newHeight = getDefaultHeight(plotsWithData) + (shouldShowLegend ? height : 0);
7647
7684
  setChartHeight(newHeight);
7648
7685
  echartsInstance.resize();
7649
- }, [option.grid, plotsWithData, shouldShowLegend]);
7686
+ }, [option.grid, plotsWithData, shouldShowLegend, onLegendHeightChange]);
7650
7687
  React.useEffect(function () {
7651
7688
  var onHeight = function onHeight() {
7652
7689
  var _chartRef$current2;
@@ -7700,7 +7737,8 @@ var TimeSeriesChart = function TimeSeriesChart(_ref2) {
7700
7737
  animation = _ref2$animation === void 0 ? false : _ref2$animation,
7701
7738
  _ref2$shouldShowLegen = _ref2.shouldShowLegend,
7702
7739
  shouldShowLegend = _ref2$shouldShowLegen === void 0 ? true : _ref2$shouldShowLegen,
7703
- onToggleLegendParam = _ref2.onToggleLegendParam;
7740
+ onToggleLegendParam = _ref2.onToggleLegendParam,
7741
+ onLegendHeightChange = _ref2.onLegendHeightChange;
7704
7742
  var _useThemeContext = useThemeContext(),
7705
7743
  namedTheme = _useThemeContext.namedTheme;
7706
7744
  var isDark = (namedTheme == null ? void 0 : namedTheme.theme.palette.mode) === 'dark';
@@ -7715,7 +7753,8 @@ var TimeSeriesChart = function TimeSeriesChart(_ref2) {
7715
7753
  var _useChartHeight = useChartHeight({
7716
7754
  plotsWithData: enabledPlots,
7717
7755
  shouldShowLegend: shouldShowLegend,
7718
- option: optionWithZoom
7756
+ option: optionWithZoom,
7757
+ onLegendHeightChange: onLegendHeightChange
7719
7758
  }),
7720
7759
  chartHeight = _useChartHeight.chartHeight,
7721
7760
  chartRef = _useChartHeight.chartRef,
@@ -7760,42 +7799,171 @@ var TimeSeriesChart = function TimeSeriesChart(_ref2) {
7760
7799
  }) : null;
7761
7800
  };
7762
7801
 
7763
- var TimeSeriesView = /*#__PURE__*/React.memo(function (_ref) {
7764
- var plotPreset = _ref.plotPreset,
7765
- selectedLocation = _ref.selectedLocation,
7766
- services = _ref.services,
7767
- splitGeoJsonByCollection = _ref.splitGeoJsonByCollection,
7768
- isLoading = _ref.isLoading,
7769
- _ref$shouldShowLegend = _ref.shouldShowLegend,
7770
- shouldShowLegend = _ref$shouldShowLegend === void 0 ? true : _ref$shouldShowLegend,
7771
- onToggleLegendParam = _ref.onToggleLegendParam,
7772
- syncgroupTimeState = _ref.syncgroupTimeState;
7802
+ var getParameterKey = function getParameterKey(param) {
7803
+ return param.serviceId + "|" + param.collectionId + "|" + param.propertyName;
7804
+ };
7805
+ var PlotErrorBanners = function PlotErrorBanners(_ref) {
7806
+ var plotsWithData = _ref.plotsWithData,
7807
+ parametersFailingInAllPlots = _ref.parametersFailingInAllPlots,
7808
+ legendHeight = _ref.legendHeight,
7809
+ getErrorTitle = _ref.getErrorTitle,
7810
+ getErrorTranslationKey = _ref.getErrorTranslationKey,
7811
+ t = _ref.t;
7812
+ return jsx(Fragment, {
7813
+ children: plotsWithData.map(function (plot, index) {
7814
+ var _plot$failedParameter, _plot$errors;
7815
+ var parametersFailingKeys = new Set(parametersFailingInAllPlots.map(getParameterKey));
7816
+ var plotSpecificFailedParameters = ((_plot$failedParameter = plot.failedParameters) == null ? void 0 : _plot$failedParameter.filter(function (param) {
7817
+ return !parametersFailingKeys.has(getParameterKey(param));
7818
+ })) || [];
7819
+ if (plotSpecificFailedParameters.length === 0) {
7820
+ return null;
7821
+ }
7822
+ var errorTitle = getErrorTitle(plotSpecificFailedParameters);
7823
+ var errorKey = ((_plot$errors = plot.errors) == null ? void 0 : _plot$errors[0]) || 'error-message-general';
7824
+ var translationKey = getErrorTranslationKey(errorKey, plotSpecificFailedParameters.length);
7825
+ var errorInfo = t(translationKey);
7826
+ return jsx(Box, {
7827
+ sx: {
7828
+ position: 'absolute',
7829
+ top: legendHeight + PLOT_HEIGHT * index + 35,
7830
+ left: 0,
7831
+ right: 0,
7832
+ zIndex: 10,
7833
+ pointerEvents: 'none'
7834
+ },
7835
+ children: jsx(Box, {
7836
+ sx: {
7837
+ pointerEvents: 'auto'
7838
+ },
7839
+ children: jsx(AlertBanner, {
7840
+ info: errorInfo,
7841
+ shouldClose: true,
7842
+ title: errorTitle
7843
+ })
7844
+ })
7845
+ }, "error-" + plot.plotId);
7846
+ })
7847
+ });
7848
+ };
7849
+ var TimeSeriesView = /*#__PURE__*/React.memo(function (_ref2) {
7850
+ var plotPreset = _ref2.plotPreset,
7851
+ selectedLocation = _ref2.selectedLocation,
7852
+ services = _ref2.services,
7853
+ splitGeoJsonByCollection = _ref2.splitGeoJsonByCollection,
7854
+ isLoading = _ref2.isLoading,
7855
+ _ref2$shouldShowLegen = _ref2.shouldShowLegend,
7856
+ shouldShowLegend = _ref2$shouldShowLegen === void 0 ? true : _ref2$shouldShowLegen,
7857
+ onToggleLegendParam = _ref2.onToggleLegendParam,
7858
+ syncgroupTimeState = _ref2.syncgroupTimeState;
7773
7859
  var _useTimeseriesTransla = useTimeseriesTranslation(),
7774
7860
  t = _useTimeseriesTransla.t;
7861
+ var _React$useState = React.useState(0),
7862
+ legendHeight = _React$useState[0],
7863
+ setLegendHeight = _React$useState[1];
7775
7864
  var _useGetPlotsWithData = useGetPlotsWithData(plotPreset, selectedLocation, services, splitGeoJsonByCollection, t, syncgroupTimeState),
7776
7865
  plotsWithData = _useGetPlotsWithData.data,
7777
- isFetchingPlots = _useGetPlotsWithData.isFetching,
7778
- error = _useGetPlotsWithData.error;
7866
+ isFetchingPlots = _useGetPlotsWithData.isFetching;
7779
7867
  React.useEffect(function () {
7780
7868
  if (isLoading) {
7781
7869
  isLoading(isFetchingPlots);
7782
7870
  }
7783
7871
  }, [isFetchingPlots, isLoading]);
7784
- return jsxs(Card, {
7872
+ var getErrorTitle = function getErrorTitle(parameters) {
7873
+ var propertyNames = parameters.map(function (param) {
7874
+ return param.propertyName;
7875
+ });
7876
+ if (propertyNames.length > 1) {
7877
+ return "\"" + propertyNames.join('", "') + "\"";
7878
+ }
7879
+ return "\"" + (propertyNames[0] || '') + "\"";
7880
+ };
7881
+ var getErrorTranslationKey = function getErrorTranslationKey(errorKey, parameterCount) {
7882
+ // Use plural translation only when multiple parameters fail with error-message-general
7883
+ return errorKey === 'error-message-general' && parameterCount > 1 ? 'error-message-general-plural' : errorKey;
7884
+ };
7885
+ var getCombinedErrorInfo = React.useCallback(function (parametersFailingInAllPlots, plotsWithData, totalPlotCount) {
7886
+ var _plotsWithData$;
7887
+ if (parametersFailingInAllPlots.length === 0 || totalPlotCount === 0) {
7888
+ return '';
7889
+ }
7890
+ var errorKey = (plotsWithData == null || (_plotsWithData$ = plotsWithData[0]) == null || (_plotsWithData$ = _plotsWithData$.errors) == null ? void 0 : _plotsWithData$[0]) || 'error-message-general';
7891
+ var translationKey = getErrorTranslationKey(errorKey, parametersFailingInAllPlots.length);
7892
+ var errorMessage = t(translationKey);
7893
+ return totalPlotCount === 1 ? errorMessage : t('all-plots') + ": " + errorMessage;
7894
+ }, [t]);
7895
+ var getParametersFailingInAllPlots = function getParametersFailingInAllPlots(plotsWithData, totalPlotCount) {
7896
+ if (!plotsWithData || totalPlotCount === 0) {
7897
+ return [];
7898
+ }
7899
+ var parameterCounts = {};
7900
+ plotsWithData.forEach(function (plot) {
7901
+ var _plot$failedParameter2;
7902
+ (_plot$failedParameter2 = plot.failedParameters) == null || _plot$failedParameter2.forEach(function (param) {
7903
+ var key = getParameterKey(param);
7904
+ if (parameterCounts[key]) {
7905
+ parameterCounts[key].count += 1;
7906
+ } else {
7907
+ parameterCounts[key] = {
7908
+ count: 1,
7909
+ param: param
7910
+ };
7911
+ }
7912
+ });
7913
+ });
7914
+ return Object.values(parameterCounts).filter(function (_ref3) {
7915
+ var count = _ref3.count;
7916
+ return count === totalPlotCount;
7917
+ }).map(function (_ref4) {
7918
+ var param = _ref4.param;
7919
+ return param;
7920
+ });
7921
+ };
7922
+ var totalPlotCount = (plotsWithData == null ? void 0 : plotsWithData.length) || 0;
7923
+ var parametersFailingInAllPlots = React.useMemo(function () {
7924
+ return getParametersFailingInAllPlots(plotsWithData, totalPlotCount);
7925
+ }, [plotsWithData, totalPlotCount]);
7926
+ var hasParametersFailingInAllPlots = parametersFailingInAllPlots.length > 0;
7927
+ var combinedErrorTitle = getErrorTitle(parametersFailingInAllPlots);
7928
+ var combinedErrorInfo = React.useMemo(function () {
7929
+ return hasParametersFailingInAllPlots ? getCombinedErrorInfo(parametersFailingInAllPlots, plotsWithData, totalPlotCount) : '';
7930
+ }, [getCombinedErrorInfo, hasParametersFailingInAllPlots, parametersFailingInAllPlots, plotsWithData, totalPlotCount]);
7931
+ return jsxs(Box, {
7785
7932
  "data-testid": "TimeSeriesView",
7786
- sx: {
7787
- border: 'solid 1px',
7788
- borderColor: 'geowebColors.cards.cardContainerBorder',
7789
- boxShadow: 'none'
7790
- },
7791
- children: [!!error && jsx(AlertBanner, {
7792
- info: "" + error.message,
7793
- shouldClose: true,
7794
- title: t('error-title')
7795
- }), !!plotsWithData && jsx(TimeSeriesChart, {
7796
- onToggleLegendParam: onToggleLegendParam,
7797
- plotsWithData: plotsWithData,
7798
- shouldShowLegend: shouldShowLegend
7933
+ children: [hasParametersFailingInAllPlots ? jsx(Card, {
7934
+ sx: {
7935
+ border: 'solid 1px',
7936
+ borderColor: 'geowebColors.cards.cardContainerBorder',
7937
+ boxShadow: 'none'
7938
+ },
7939
+ children: jsx(Box, {
7940
+ "data-testid": "TimeSeriesView-alert-banner",
7941
+ children: jsx(AlertBanner, {
7942
+ info: combinedErrorInfo,
7943
+ shouldClose: true,
7944
+ title: combinedErrorTitle
7945
+ })
7946
+ })
7947
+ }) : null, !!plotsWithData && plotsWithData.length > 0 && jsxs(Card, {
7948
+ sx: {
7949
+ border: 'solid 1px',
7950
+ borderColor: 'geowebColors.cards.cardContainerBorder',
7951
+ boxShadow: 'none',
7952
+ position: 'relative'
7953
+ },
7954
+ children: [!!plotsWithData && plotsWithData.length > 0 && jsx(PlotErrorBanners, {
7955
+ getErrorTitle: getErrorTitle,
7956
+ getErrorTranslationKey: getErrorTranslationKey,
7957
+ legendHeight: legendHeight,
7958
+ parametersFailingInAllPlots: parametersFailingInAllPlots,
7959
+ plotsWithData: plotsWithData,
7960
+ t: t
7961
+ }), jsx(TimeSeriesChart, {
7962
+ onLegendHeightChange: setLegendHeight,
7963
+ onToggleLegendParam: onToggleLegendParam,
7964
+ plotsWithData: plotsWithData,
7965
+ shouldShowLegend: shouldShowLegend
7966
+ })]
7799
7967
  })]
7800
7968
  });
7801
7969
  }, function (previousProps, nextProps) {
@@ -7804,8 +7972,9 @@ var TimeSeriesView = /*#__PURE__*/React.memo(function (_ref) {
7804
7972
  var sameLocation = isEqual(previousProps.selectedLocation, nextProps.selectedLocation);
7805
7973
  var sameGeoJson = isEqual(previousProps.splitGeoJsonByCollection, nextProps.splitGeoJsonByCollection);
7806
7974
  var sameLegend = previousProps.shouldShowLegend === nextProps.shouldShowLegend;
7975
+ var sameServices = isEqual(previousProps.services, nextProps.services);
7807
7976
  var sameTime = ((_previousProps$syncgr = previousProps.syncgroupTimeState) == null ? void 0 : _previousProps$syncgr.start) === ((_nextProps$syncgroupT = nextProps.syncgroupTimeState) == null ? void 0 : _nextProps$syncgroupT.start) && ((_previousProps$syncgr2 = previousProps.syncgroupTimeState) == null ? void 0 : _previousProps$syncgr2.end) === ((_nextProps$syncgroupT2 = nextProps.syncgroupTimeState) == null ? void 0 : _nextProps$syncgroupT2.end);
7808
- return samePreset && sameLocation && sameGeoJson && sameLegend && sameTime;
7977
+ return samePreset && sameLocation && sameGeoJson && sameLegend && sameServices && sameTime;
7809
7978
  });
7810
7979
 
7811
7980
  var TOOLTIP_TITLE = 'Timeseries Manager';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/time-series",
3
- "version": "15.3.0",
3
+ "version": "16.0.0",
4
4
  "description": "GeoWeb time-series library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -9,25 +9,25 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "immer": "^10.0.3",
12
- "@opengeoweb/shared": "15.3.0",
12
+ "@opengeoweb/shared": "16.0.0",
13
13
  "@reduxjs/toolkit": "^2.6.1",
14
14
  "react-redux": "^9.2.0",
15
- "@opengeoweb/webmap": "15.3.0",
16
- "@opengeoweb/webmap-react": "15.3.0",
15
+ "@opengeoweb/webmap": "16.0.0",
16
+ "@opengeoweb/webmap-react": "16.0.0",
17
17
  "lodash": "^4.17.21",
18
- "@opengeoweb/api": "15.3.0",
19
- "@opengeoweb/theme": "15.3.0",
20
- "@opengeoweb/store": "15.3.0",
18
+ "@opengeoweb/api": "16.0.0",
19
+ "@opengeoweb/theme": "16.0.0",
20
+ "@opengeoweb/store": "16.0.0",
21
21
  "echarts-for-react": "^3.0.2",
22
22
  "echarts": "^5.4.3",
23
23
  "react-sortablejs": "^6.1.4",
24
- "@opengeoweb/snackbar": "15.3.0",
24
+ "@opengeoweb/snackbar": "16.0.0",
25
25
  "react-window": "^2.2.6",
26
26
  "msw": "^2.7.3",
27
27
  "i18next": "^25.0.1",
28
28
  "react-i18next": "^15.1.1",
29
- "@opengeoweb/core": "15.3.0",
30
- "@opengeoweb/form-fields": "15.3.0",
29
+ "@opengeoweb/core": "16.0.0",
30
+ "@opengeoweb/form-fields": "16.0.0",
31
31
  "@mui/material": "^7.0.1",
32
32
  "react-hook-form": "^7.61.1",
33
33
  "uuid": "^10.0.0",
@@ -1,10 +1,11 @@
1
1
  import type { EChartsInstance, EChartsOption } from 'echarts-for-react';
2
2
  import React from 'react';
3
3
  import type { PlotWithData } from './types';
4
- export declare const useChartHeight: ({ plotsWithData, shouldShowLegend, option, }: {
4
+ export declare const useChartHeight: ({ plotsWithData, shouldShowLegend, option, onLegendHeightChange, }: {
5
5
  plotsWithData: PlotWithData[];
6
6
  shouldShowLegend: boolean;
7
7
  option: EChartsOption;
8
+ onLegendHeightChange?: (height: number) => void;
8
9
  }) => {
9
10
  chartHeight: number;
10
11
  chartRef: React.RefObject<EChartsInstance>;
@@ -28,4 +29,5 @@ export declare const TimeSeriesChart: React.FC<{
28
29
  animation?: boolean;
29
30
  shouldShowLegend?: boolean;
30
31
  onToggleLegendParam?: (legendId: string) => void;
32
+ onLegendHeightChange?: (height: number) => void;
31
33
  }>;
@@ -10,6 +10,11 @@ export interface Properties {
10
10
  propertyName: string;
11
11
  values: number[];
12
12
  }
13
+ export interface FailedParameterInfo {
14
+ propertyName: string;
15
+ serviceId: string;
16
+ collectionId: string;
17
+ }
13
18
  export interface UseGetLocationReturnTypeProperties {
14
19
  name?: string;
15
20
  detail?: string;
@@ -47,6 +52,8 @@ export interface Plot {
47
52
  }
48
53
  export interface PlotWithData extends Plot {
49
54
  parametersWithData: ParameterWithData[];
55
+ errors?: string[];
56
+ failedParameters?: FailedParameterInfo[];
50
57
  }
51
58
  export type PlotType = 'bar' | 'line' | 'scatter';
52
59
  export interface Parameter {
@@ -1,12 +1,16 @@
1
1
  import type { FeatureCollection, GeometryObject } from 'geojson';
2
2
  import type { Plot, PlotParameter, PlotPreset, TimeSeriesService } from '@opengeoweb/shared';
3
3
  import type { syncGroupsTypes } from '@opengeoweb/store';
4
- import type { EDRPositionResponse, EDRPositionResponseCoverageCollection, GeoJsonWithService, Parameter, ParameterWithData, PlotWithData, PointerLocation, TimeSeriesLocation, UseGetLocationReturnTypeProperties } from './types';
4
+ import type { EDRPositionResponse, EDRPositionResponseCoverageCollection, FailedParameterInfo, GeoJsonWithService, Parameter, ParameterWithData, PlotWithData, PointerLocation, TimeSeriesLocation, UseGetLocationReturnTypeProperties } from './types';
5
+ /** Maps plot ID to array of error message strings */
6
+ export type PlotErrorsMap = Record<Plot['plotId'], string[]>;
7
+ /** Maps plot ID to array of failed parameter information */
8
+ export type FailedParametersMap = Record<Plot['plotId'], FailedParameterInfo[]>;
5
9
  export declare const createGeoJsonArrays: (locations: (FeatureCollection<GeometryObject, UseGetLocationReturnTypeProperties> | undefined)[], services: TimeSeriesService[] | undefined, hideFeatures: boolean, memoizedSelectedFeature: TimeSeriesLocation | null) => {
6
10
  combinedGeoJson: FeatureCollection<GeometryObject, syncGroupsTypes.PotentialDataProperties>;
7
11
  };
8
12
  export declare const getNearbyLocation: (parameter: Parameter, service: TimeSeriesService, selectedLocation: PointerLocation, geojsonMap: Map<string, GeoJsonWithService>) => TimeSeriesLocation | null;
9
- export declare const getPlotsWithParameters: (plots: Plot[], parameters: ParameterWithData[]) => PlotWithData[];
13
+ export declare const getPlotsWithParameters: (plots: Plot[], parameters: ParameterWithData[], plotErrors?: PlotErrorsMap, failedParameters?: FailedParametersMap) => PlotWithData[];
10
14
  export declare function consolidateFeatures<P>(features: {
11
15
  id: string;
12
16
  geoJSON: FeatureCollection<GeometryObject, P>;
@@ -11,6 +11,7 @@ interface CombinedResult<T> {
11
11
  data: T | undefined;
12
12
  isFetching: boolean;
13
13
  error: Error | null | undefined;
14
+ parameterErrors?: Record<number, string>;
14
15
  }
15
16
  export declare const timeseriesEDRQueryOptions: {
16
17
  readonly getServiceInformationQuery: (serviceUrl: string) => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<EDRServiceInformation, Error, EDRServiceInformation, string[]>, "queryFn"> & {