@geo2france/api-dashboard 1.10.0 → 1.11.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/dist/components/Charts/ChartEcharts.d.ts +3 -3
- package/dist/components/Charts/ChartEcharts.js +2 -2
- package/dist/components/Charts/Statistics.d.ts +11 -0
- package/dist/components/Charts/Statistics.js +14 -3
- package/dist/components/DashboardPage/Block.js +2 -1
- package/dist/components/Layout/Error.d.ts +5 -0
- package/dist/components/Layout/Error.js +8 -2
- package/dist/components/Map/Map.js +2 -2
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EChartsOption } from "echarts";
|
|
2
|
-
import EChartsReact from "echarts-for-react";
|
|
3
|
-
interface ChartEchartsProps {
|
|
4
|
-
option
|
|
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 } })] }),
|
|
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:
|
|
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);
|
|
@@ -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
|
+
};
|
|
@@ -64,7 +64,7 @@ export const MapLayer = ({ dataset, categoryKey, color = 'red', type = 'circle',
|
|
|
64
64
|
: data?.geojson; // Sinon on utilise le geojson (fournisseur wfs)
|
|
65
65
|
const geom_type = geojson?.features?.[0] && getType(geojson?.features?.[0]);
|
|
66
66
|
/** Type de données dans categoryKey (string ou number) */
|
|
67
|
-
const type_value = categoryKey && typeof (data?.data?.[0][categoryKey]);
|
|
67
|
+
const type_value = categoryKey && typeof (data?.data?.[0]?.[categoryKey]);
|
|
68
68
|
/** Valeurs distinctes (si type string) */
|
|
69
69
|
const values = (type_value === 'string') && categoryKey && data?.data && from(data?.data).rollup({ a: op.array_agg_distinct(categoryKey) }).get('a', 0) || undefined;
|
|
70
70
|
/** Couleurs de la palette */
|
|
@@ -105,7 +105,7 @@ export const MapLayer = ({ dataset, categoryKey, color = 'red', type = 'circle',
|
|
|
105
105
|
}
|
|
106
106
|
//devnote : regarder la colonne contenant les valeurs pour proposer une représentation (catégorie ou choroplèthe)
|
|
107
107
|
useEffect(() => {
|
|
108
|
-
if (geojson) {
|
|
108
|
+
if (geojson && geojson.features.length > 0) { // do not fitbound if no features
|
|
109
109
|
const box = bbox(geojson).slice(0, 4);
|
|
110
110
|
map?.fitBounds(box, { padding: 20 });
|
|
111
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geo2france/api-dashboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
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"
|