@geo2france/api-dashboard 1.10.1 → 1.11.1

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,7 +1,7 @@
1
1
  import { EChartsOption } from "echarts";
2
- import EChartsReact from "echarts-for-react";
3
- interface ChartEchartsProps {
4
- option?: EChartsOption;
2
+ import EChartsReact, { EChartsReactProps } from "echarts-for-react";
3
+ interface ChartEchartsProps extends EChartsReactProps {
4
+ option: EChartsOption;
5
5
  }
6
6
  export declare const ChartEcharts: import("react").ForwardRefExoticComponent<ChartEchartsProps & import("react").RefAttributes<EChartsReact>>;
7
7
  export {};
@@ -11,7 +11,7 @@ const { useToken } = theme;
11
11
  * - Utilise le style de texte de l'application
12
12
  * devnote : A partir de React 19, ne plus utiliser forwardRef https://react.dev/reference/react/forwardRef
13
13
  */
14
- export const ChartEcharts = forwardRef(({ option = {} }, ref) => {
14
+ export const ChartEcharts = forwardRef(({ option = {}, ...restProps }, ref) => {
15
15
  const innerRef = useRef(null);
16
16
  useImperativeHandle(ref, () => innerRef.current, []); // Pour exposer le innerref au parent
17
17
  const { token } = useToken();
@@ -31,5 +31,5 @@ export const ChartEcharts = forwardRef(({ option = {} }, ref) => {
31
31
  axisLine: { lineStyle: { color: token.colorTextSecondary } },
32
32
  },
33
33
  };
34
- return (_jsx(EChartsReact, { option: deepMerge({}, default_option, option), ref: innerRef }));
34
+ return (_jsx(EChartsReact, { option: deepMerge({}, default_option, option), ref: innerRef, ...restProps }));
35
35
  });
@@ -1,5 +1,14 @@
1
1
  import { ReactElement } from "react";
2
+ import { SimpleRecord } from "../../types";
2
3
  type comparwithType = "first" | "previous";
4
+ interface annotation_params_type {
5
+ /** Valeur principale */
6
+ value: number;
7
+ /** Jeu de données utilisé */
8
+ data: SimpleRecord[] | undefined;
9
+ /** Valeur de comparaison */
10
+ compareValue: number;
11
+ }
3
12
  interface StatisticsProps {
4
13
  /** Identifiant du jeu de données */
5
14
  dataset: string;
@@ -23,6 +32,8 @@ interface StatisticsProps {
23
32
  help?: string;
24
33
  /** Comparer la valeur avec la précédente ou la première du jeu de données */
25
34
  compareWith?: comparwithType;
35
+ /** Texte d'annotation (remplace evolution si définie) */
36
+ annotation?: React.ReactNode | ((param: annotation_params_type) => React.ReactNode);
26
37
  }
27
38
  /**
28
39
  * Composant `Statistics` affichant une valeur d'un dataset avec son évolution.
@@ -20,15 +20,23 @@ const { Text, Paragraph } = Typography;
20
20
  * @param {StatisticsProps} props - Propriétés du composant
21
21
  * @returns {ReactElement} Carte statistique
22
22
  */
23
- export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix, title, icon: icon_input, color, invertColor = false, help, compareWith, relativeEvolution = false }) => {
23
+ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix, title, icon: icon_input, color, invertColor = false, help, compareWith, relativeEvolution = false, annotation }) => {
24
24
  const icon = typeof icon_input === "string" ? _jsx(Icon, { icon: icon_input }) : icon_input;
25
25
  const dataset = useDataset(dataset_id);
26
- const value = dataset?.data?.slice(-1)[0][dataKey]; // Dernière valeur du dataset
26
+ const value = dataset?.data?.slice(-1)[0][dataKey]; // Dernière valeur du dataset. Caster en Number ?
27
27
  const compare_value = compareWith === 'previous' ? dataset?.data?.slice(-2)[0][dataKey] : dataset?.data?.slice(0, 1)[0][dataKey]; //Première ou avant dernière
28
28
  const evolution = relativeEvolution ? 100 * ((value - compare_value) / compare_value) : value - compare_value;
29
29
  const evolution_unit = relativeEvolution ? '%' : unit;
30
30
  const evolution_is_good = invertColor ? evolution < 0 : evolution > 0;
31
31
  const tooltip = help && _jsx(Tooltip, { title: help, children: _jsx(QuestionCircleOutlined, {}) });
32
+ const annotation_params = { value: value || NaN, compareValue: compare_value || NaN, data: dataset?.data || [] };
33
+ let subtitle;
34
+ if (annotation !== undefined) {
35
+ subtitle = typeof annotation === 'function' ? annotation(annotation_params) : annotation;
36
+ }
37
+ else if (evolution) {
38
+ subtitle = (_jsxs(Paragraph, { style: { marginBottom: "0.5rem" }, children: [_jsxs(Text, { strong: true, type: evolution_is_good ? "success" : "danger", style: { fontSize: "120%" }, children: [evolution < 0.1 ? '' : '+', evolution.toLocaleString(undefined, { maximumFractionDigits: 1 }), "\u00A0", evolution_unit] }), " ", _jsx(Text, { italic: true, type: "secondary", children: evolutionSuffix })] }));
39
+ }
32
40
  return (_jsx(Card, { title: title, style: {
33
41
  borderLeft: `4px solid ${color}`,
34
42
  height: "100%"
@@ -44,7 +52,10 @@ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix
44
52
  fontSize: 14,
45
53
  minHeight: 35,
46
54
  },
47
- }, extra: tooltip, children: _jsxs(Flex, { vertical: true, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsxs(Text, { style: { fontSize: "150%", paddingTop: 8, paddingBottom: 8, paddingLeft: 0 }, children: [value?.toLocaleString(), " ", unit] }), icon && _jsx(Avatar, { size: 32 + 8, icon: icon, style: { backgroundColor: color } })] }), evolution && _jsxs(Paragraph, { style: { marginBottom: "0.5rem" }, children: [_jsxs(Text, { strong: true, type: evolution_is_good ? "success" : "danger", style: { fontSize: "120%" }, children: [evolution < 0.1 ? '' : '+', evolution.toLocaleString(undefined, { maximumFractionDigits: 1 }), "\u00A0", evolution_unit] }), " ", _jsx(Text, { italic: true, type: "secondary", children: evolutionSuffix })] })] }) }));
55
+ }, extra: tooltip, children: _jsxs(Flex, { vertical: true, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsxs(Text, { style: { fontSize: "150%", paddingTop: 8, paddingBottom: 8, paddingLeft: 0 }, children: [value?.toLocaleString(), " ", unit] }), icon && _jsx(Avatar, { size: 32 + 8, icon: icon, style: { backgroundColor: color } })] }), typeof subtitle == 'string' ?
56
+ _jsx(Text, { italic: true, type: "secondary", children: subtitle })
57
+ :
58
+ _jsx("div", { children: subtitle })] }) }));
48
59
  };
49
60
  /**
50
61
  * `StatisticsCollection` permet de regrouper plusieurs cartes statistiques
@@ -4,6 +4,7 @@ import { createContext, useContext, useEffect, useId, useState } from "react";
4
4
  import { Icon } from "@iconify/react";
5
5
  import { ProducersFooter } from "../Dataset/Producer";
6
6
  import { MoreOutlined } from '@ant-design/icons';
7
+ import { ErrorBoundary } from "../Layout/Error";
7
8
  const { useToken } = theme;
8
9
  export const ChartBlockContext = createContext(undefined);
9
10
  export const DSL_ChartBlock = ({ children }) => {
@@ -36,7 +37,7 @@ export const DSL_ChartBlock = ({ children }) => {
36
37
  };
37
38
  DL();
38
39
  };
39
- return (_jsx(ChartBlockContext.Provider, { value: { config: config, setConfig: (e) => setConfig(e) }, children: _jsxs(Card, { style: { height: '100%' }, extra: has_action && dropdown_toolbox, title: config.title, children: [children, _jsx(ProducersFooter, { component: children })] }) }));
40
+ return (_jsx(ChartBlockContext.Provider, { value: { config: config, setConfig: (e) => setConfig(e) }, children: _jsx(Card, { style: { height: '100%' }, extra: has_action && dropdown_toolbox, title: config.title, children: _jsxs(ErrorBoundary, { children: [children, _jsx(ProducersFooter, { component: children })] }) }) }));
40
41
  };
41
42
  export const useBlockConfig = ({ title, dataExport }) => {
42
43
  const blockContext = useContext(ChartBlockContext);
@@ -37,8 +37,8 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
37
37
  inner: { left: false, right: false },
38
38
  }[join_type] ?? { left: false, right: false };
39
39
  const otherData = allDatasets?.find((d) => d.id === props.dataset)?.data;
40
- if (!otherData)
41
- return undefined; // ou [] ou data selon la logique souhaitée
40
+ if (!otherData || otherData.length < 1)
41
+ return undefined; // prevent arquero from crash on missed data
42
42
  return from(data).join(from(otherData), props.joinKey, undefined, aq_join_option).objects();
43
43
  };
44
44
  return funct;
@@ -20,5 +20,5 @@ export const Debug = () => {
20
20
  label: _jsxs("span", { children: [_jsx(DatasetBadgeStatus, { isError: dataset?.isError, isFetching: dataset?.isFetching }), " ", dataset.id, " ", " ", _jsxs(Text, { type: "secondary", children: [" ", dataset?.resource, " "] }), " ", _jsx(Badge, { color: token.colorInfo, overflowCount: 9999, count: dataset?.data?.length })] }),
21
21
  children: _jsx(DataPreview, { dataset: dataset.id, pageSize: 3 })
22
22
  }));
23
- return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(BugOutlined, {}), type: "primary", onClick: () => setIsModalOpen(true), style: { top: 5 }, className: "debugFloatButton" }), ";", _jsxs(Modal, { title: "Information concepteur", width: "90%", centered: true, styles: { content: { 'width': "100%", padding: 36 } }, closable: { 'aria-label': 'Custom Close Button' }, open: isModalOpen, onCancel: () => setIsModalOpen(false), footer: null, children: [_jsx(Title, { level: 5, children: "Jeux de donn\u00E9es " }), _jsx(Collapse, { accordion: true, items: items }), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Contr\u00F4les utilisateur " }), _jsx(ControlPreview, {}), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Palette " }), _jsx(PalettePreview, {})] })] }));
23
+ return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(BugOutlined, {}), type: "primary", onClick: () => setIsModalOpen(true), style: { top: 5 }, className: "debugFloatButton" }), _jsxs(Modal, { title: "Information concepteur", width: "90%", centered: true, styles: { content: { 'width': "100%", padding: 36 } }, closable: { 'aria-label': 'Custom Close Button' }, open: isModalOpen, onCancel: () => setIsModalOpen(false), footer: null, children: [_jsx(Title, { level: 5, children: "Jeux de donn\u00E9es " }), _jsx(Collapse, { accordion: true, items: items }), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Contr\u00F4les utilisateur " }), _jsx(ControlPreview, {}), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Palette " }), _jsx(PalettePreview, {})] })] }));
24
24
  };
@@ -1,2 +1,7 @@
1
1
  import React from "react";
2
2
  export declare const ErrorComponent: React.FC;
3
+ interface ErrorBoundaryProps {
4
+ children?: React.ReactNode;
5
+ }
6
+ export declare const ErrorBoundary: React.FC<ErrorBoundaryProps>;
7
+ export {};
@@ -1,6 +1,12 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Result, Typography, Space } from "antd";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Result, Typography, Space, Alert } from "antd";
3
+ import { ErrorBoundary as ErrorBoundaryBase } from "react-error-boundary";
4
+ import { Icon } from "@iconify/react";
3
5
  const { Text } = Typography;
4
6
  export const ErrorComponent = () => {
5
7
  return (_jsx(Result, { status: "404", title: "404", extra: _jsx(Space, { direction: "vertical", size: "large", children: _jsx(Space, { children: _jsx(Text, { children: "La page n'existe pas" }) }) }) }));
6
8
  };
9
+ export const ErrorBoundary = ({ children }) => {
10
+ const fallback = (_jsx(Alert, { message: _jsx(Icon, { icon: "garden:face-very-sad-stroke-12", fontSize: 25 }), description: _jsxs("div", { children: [_jsx("p", { children: " Cette visualisation ne peux malheureusement pas s'afficher." }), _jsx("p", { children: " Contactez l'administrateur\u00B7rice du tableau de bord si le probl\u00E8me persiste." })] }), type: "warning" }));
11
+ return (_jsx(ErrorBoundaryBase, { fallback: fallback, children: children }));
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geo2france/api-dashboard",
3
- "version": "1.10.1",
3
+ "version": "1.11.1",
4
4
  "private": false,
5
5
  "description": "Build dashboards with JSX/TSX",
6
6
  "main": "dist/index.js",
@@ -56,6 +56,7 @@
56
56
  "echarts": "^6.0.0",
57
57
  "echarts-for-react": "^3.0.4",
58
58
  "query-string": "~7.1.3",
59
+ "react-error-boundary": "^6.0.0",
59
60
  "react-helmet": "^6.1.0",
60
61
  "react-helmet-async": "^2.0.5",
61
62
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"