@geo2france/api-dashboard 1.11.1 → 1.12.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/Statistics.d.ts +6 -2
- package/dist/components/Charts/Statistics.js +6 -5
- package/dist/components/DashboardPage/Page.d.ts +1 -0
- package/dist/components/Dataset/Dataset.js +28 -6
- package/dist/components/Dataset/hooks.d.ts +3 -0
- package/dist/data_providers/wfs/utils/mapOperator.d.ts +1 -0
- package/dist/data_providers/wfs/utils/mapOperator.js +36 -0
- package/dist/utils/hash_data.d.ts +9 -0
- package/dist/utils/hash_data.js +20 -0
- package/dist/utils/useBlockConfig.d.ts +7 -0
- package/dist/utils/useBlockConfig.js +9 -0
- package/package.json +1 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ReactElement } from "react";
|
|
2
2
|
import { SimpleRecord } from "../../types";
|
|
3
3
|
type comparwithType = "first" | "previous";
|
|
4
|
-
interface
|
|
4
|
+
interface ICallbackParams {
|
|
5
5
|
/** Valeur principale */
|
|
6
6
|
value: number;
|
|
7
7
|
/** Jeu de données utilisé */
|
|
8
8
|
data: SimpleRecord[] | undefined;
|
|
9
|
+
/** Ligne courante (pour accéder aux autres champs) */
|
|
10
|
+
row: SimpleRecord | undefined;
|
|
9
11
|
/** Valeur de comparaison */
|
|
10
12
|
compareValue: number;
|
|
11
13
|
}
|
|
@@ -33,7 +35,9 @@ interface StatisticsProps {
|
|
|
33
35
|
/** Comparer la valeur avec la précédente ou la première du jeu de données */
|
|
34
36
|
compareWith?: comparwithType;
|
|
35
37
|
/** Texte d'annotation (remplace evolution si définie) */
|
|
36
|
-
annotation?: React.ReactNode | ((param:
|
|
38
|
+
annotation?: React.ReactNode | ((param: ICallbackParams) => React.ReactNode);
|
|
39
|
+
/** Fonction a appliquer avant rendu */
|
|
40
|
+
valueFormatter?: ((param: ICallbackParams) => React.ReactNode);
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* Composant `Statistics` affichant une valeur d'un dataset avec son évolution.
|
|
@@ -20,19 +20,20 @@ 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, annotation }) => {
|
|
23
|
+
export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix, title, icon: icon_input, color, invertColor = false, help, compareWith, relativeEvolution = false, valueFormatter = (param) => (param.value.toLocaleString()), 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
|
|
26
|
+
const row = dataset?.data?.slice(-1)[0];
|
|
27
|
+
const value = row?.[dataKey]; // Dernière valeur du dataset. Caster en Number ?
|
|
27
28
|
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
29
|
const evolution = relativeEvolution ? 100 * ((value - compare_value) / compare_value) : value - compare_value;
|
|
29
30
|
const evolution_unit = relativeEvolution ? '%' : unit;
|
|
30
31
|
const evolution_is_good = invertColor ? evolution < 0 : evolution > 0;
|
|
31
32
|
const tooltip = help && _jsx(Tooltip, { title: help, children: _jsx(QuestionCircleOutlined, {}) });
|
|
32
|
-
const
|
|
33
|
+
const CallbackParams = { value: value || NaN, compareValue: compare_value || NaN, data: dataset?.data || [], row: row };
|
|
33
34
|
let subtitle;
|
|
34
35
|
if (annotation !== undefined) {
|
|
35
|
-
subtitle = typeof annotation === 'function' ? annotation(
|
|
36
|
+
subtitle = typeof annotation === 'function' ? annotation(CallbackParams) : annotation;
|
|
36
37
|
}
|
|
37
38
|
else if (evolution) {
|
|
38
39
|
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 })] }));
|
|
@@ -52,7 +53,7 @@ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix
|
|
|
52
53
|
fontSize: 14,
|
|
53
54
|
minHeight: 35,
|
|
54
55
|
},
|
|
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: [
|
|
56
|
+
}, 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: [valueFormatter(CallbackParams), " ", unit] }), icon && _jsx(Avatar, { size: 32 + 8, icon: icon, style: { backgroundColor: color } })] }), typeof subtitle == 'string' ?
|
|
56
57
|
_jsx(Text, { italic: true, type: "secondary", children: subtitle })
|
|
57
58
|
:
|
|
58
59
|
_jsx("div", { children: subtitle })] }) }));
|
|
@@ -4,11 +4,12 @@ import { useApi } from "../..";
|
|
|
4
4
|
import { ControlContext, DatasetRegistryContext } from "../DashboardPage/Page";
|
|
5
5
|
import { Producer } from "./Producer";
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { Filter, Transform, useAllDatasets } from "../../dsl";
|
|
7
|
+
import { Filter, Transform, useAllDatasets, useDatasets } from "../../dsl";
|
|
8
8
|
import alasql from "alasql";
|
|
9
9
|
import { DataProviderContext, getProviderFromType } from "./Provider";
|
|
10
10
|
import { Join } from "./Join";
|
|
11
11
|
import { from } from "arquero";
|
|
12
|
+
import hashCode from "../../utils/hash_data";
|
|
12
13
|
export const DSL_Dataset = ({ children, id, provider: provider_input, type: providerType = 'file', url: providerUrl, resource, pageSize, meta }) => {
|
|
13
14
|
const getTransformerFn = (component) => {
|
|
14
15
|
/*
|
|
@@ -37,9 +38,16 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
37
38
|
inner: { left: false, right: false },
|
|
38
39
|
}[join_type] ?? { left: false, right: false };
|
|
39
40
|
const otherData = allDatasets?.find((d) => d.id === props.dataset)?.data;
|
|
40
|
-
if
|
|
41
|
+
// Return undefinied if otherData is Fetching ?
|
|
42
|
+
if (!otherData || !data)
|
|
41
43
|
return undefined; // prevent arquero from crash on missed data
|
|
42
|
-
|
|
44
|
+
//fallback if one of dataset is empty. Build a 1 row table with existing joinKey
|
|
45
|
+
const [leftKey, rightKey] = Array.isArray(props.joinKey) // joinKey can be a string or un string[]
|
|
46
|
+
? props.joinKey
|
|
47
|
+
: [props.joinKey, props.joinKey];
|
|
48
|
+
const leftTable = data.length >= 1 ? data : [{ [leftKey]: null }];
|
|
49
|
+
const rightTable = otherData.length >= 1 ? otherData : [{ [rightKey]: null }];
|
|
50
|
+
return from(leftTable).join(from(rightTable), props.joinKey, undefined, aq_join_option).objects();
|
|
43
51
|
};
|
|
44
52
|
return funct;
|
|
45
53
|
}
|
|
@@ -49,7 +57,6 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
49
57
|
};
|
|
50
58
|
const datasetRegistryContext = useContext(DatasetRegistryContext);
|
|
51
59
|
const allDatasets = useAllDatasets();
|
|
52
|
-
const someFetching = !!allDatasets?.some(d => d.isFetching);
|
|
53
60
|
const controlContext = useContext(ControlContext);
|
|
54
61
|
const controls = controlContext?.values;
|
|
55
62
|
const providerContext = useContext(DataProviderContext);
|
|
@@ -70,12 +77,15 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
70
77
|
});
|
|
71
78
|
});
|
|
72
79
|
const { data, isFetching, isError } = useApi({ dataProvider: provider, resource: resource, filters: filters, pagination: { pageSize: pageSize }, meta: meta });
|
|
80
|
+
const dep_dataset_id = []; // Dependencies
|
|
73
81
|
const transformers = [];
|
|
74
82
|
/* Récuperer les fonctions transformers */
|
|
75
83
|
React.Children.toArray(children)
|
|
76
84
|
.filter((c) => React.isValidElement(c))
|
|
77
85
|
.filter((c) => typeof c.type != 'string' && (c.type.name == Transform.name || c.type.name == Join.name)).forEach((c) => {
|
|
78
86
|
transformers.push(getTransformerFn(c));
|
|
87
|
+
if (typeof c.type != 'string' && c.type.name == Join.name)
|
|
88
|
+
dep_dataset_id.push(c.props.dataset); // Add joint dataset in dep list
|
|
79
89
|
});
|
|
80
90
|
const producers = [];
|
|
81
91
|
React.Children.toArray(children)
|
|
@@ -83,12 +93,24 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
83
93
|
.filter((c) => typeof c.type != 'string' && c.type.name == Producer.name).forEach((c) => {
|
|
84
94
|
producers.push({ nom: c.props.children, url: c.props.url });
|
|
85
95
|
});
|
|
96
|
+
const depDataset = useDatasets(dep_dataset_id);
|
|
97
|
+
const depDataHash = depDataset?.map(d => d?.dataHash); //Build hash array from data
|
|
98
|
+
const someDepsAreFetching = depDataset?.some(d => d?.isFetching);
|
|
86
99
|
useEffect(() => {
|
|
87
100
|
const finalData = data?.data && transformers.reduce((datat, fn) => fn(datat), data.data);
|
|
88
101
|
if (datasetRegistryContext) {
|
|
89
|
-
datasetRegistryContext({
|
|
102
|
+
datasetRegistryContext({
|
|
103
|
+
id: id,
|
|
104
|
+
resource: resource,
|
|
105
|
+
data: finalData,
|
|
106
|
+
isFetching: !!isFetching || !!someDepsAreFetching,
|
|
107
|
+
isError: isError,
|
|
108
|
+
producers: producers,
|
|
109
|
+
geojson: data?.geojson,
|
|
110
|
+
dataHash: hashCode(finalData)
|
|
111
|
+
});
|
|
90
112
|
//Ajouter une info pour distinguer les erreurs du fourniseurs et celles des transformers ?
|
|
91
113
|
}
|
|
92
|
-
}, [resource, data, isFetching,
|
|
114
|
+
}, [resource, data, (!!isFetching || !!someDepsAreFetching), hashCode(depDataHash), children]);
|
|
93
115
|
return (_jsx(_Fragment, { children: children }));
|
|
94
116
|
};
|
|
@@ -6,6 +6,7 @@ export declare const useDataset: (dataset_id?: string) => {
|
|
|
6
6
|
isError: boolean;
|
|
7
7
|
producers?: any[];
|
|
8
8
|
geojson?: any;
|
|
9
|
+
dataHash?: number;
|
|
9
10
|
} | undefined;
|
|
10
11
|
export declare const useDatasets: (dataset_ids?: string[]) => {
|
|
11
12
|
id: string;
|
|
@@ -15,6 +16,7 @@ export declare const useDatasets: (dataset_ids?: string[]) => {
|
|
|
15
16
|
isError: boolean;
|
|
16
17
|
producers?: any[];
|
|
17
18
|
geojson?: any;
|
|
19
|
+
dataHash?: number;
|
|
18
20
|
}[] | undefined;
|
|
19
21
|
export declare const useAllDatasets: () => {
|
|
20
22
|
id: string;
|
|
@@ -24,4 +26,5 @@ export declare const useAllDatasets: () => {
|
|
|
24
26
|
isError: boolean;
|
|
25
27
|
producers?: any[];
|
|
26
28
|
geojson?: any;
|
|
29
|
+
dataHash?: number;
|
|
27
30
|
}[] | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const mapOperator: (operator: any) => string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const mapOperator = (operator) => {
|
|
2
|
+
switch (operator) {
|
|
3
|
+
case "ne":
|
|
4
|
+
return '<>';
|
|
5
|
+
case "gte":
|
|
6
|
+
return '>=';
|
|
7
|
+
case "gt":
|
|
8
|
+
return '>';
|
|
9
|
+
case "lte":
|
|
10
|
+
return `<=`;
|
|
11
|
+
case "lt":
|
|
12
|
+
return `<`;
|
|
13
|
+
case "eq":
|
|
14
|
+
return "=";
|
|
15
|
+
case "contains":
|
|
16
|
+
case "startswith":
|
|
17
|
+
case "endswith":
|
|
18
|
+
return "ilike";
|
|
19
|
+
case "containss":
|
|
20
|
+
case "startswiths":
|
|
21
|
+
case "endswiths":
|
|
22
|
+
return "like";
|
|
23
|
+
case "ncontains":
|
|
24
|
+
case "nstartswith":
|
|
25
|
+
case "nendswith":
|
|
26
|
+
return "not ilike";
|
|
27
|
+
case "ncontainss":
|
|
28
|
+
case "nstartswiths":
|
|
29
|
+
case "nendswiths":
|
|
30
|
+
return "not like";
|
|
31
|
+
case "in":
|
|
32
|
+
return operator;
|
|
33
|
+
default:
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a hash code from a string
|
|
3
|
+
* From https://stackoverflow.com/a/8831937/10995624
|
|
4
|
+
* @param {String} str The string to hash.
|
|
5
|
+
* @return {Number} A 32bit integer
|
|
6
|
+
* @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
|
7
|
+
*/
|
|
8
|
+
declare function hashCode(input: any): number | undefined;
|
|
9
|
+
export default hashCode;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a hash code from a string
|
|
3
|
+
* From https://stackoverflow.com/a/8831937/10995624
|
|
4
|
+
* @param {String} str The string to hash.
|
|
5
|
+
* @return {Number} A 32bit integer
|
|
6
|
+
* @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
|
7
|
+
*/
|
|
8
|
+
function hashCode(input) {
|
|
9
|
+
if (input === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
let hash = 0;
|
|
12
|
+
const str = JSON.stringify(input);
|
|
13
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
14
|
+
let chr = str.charCodeAt(i);
|
|
15
|
+
hash = (hash << 5) - hash + chr;
|
|
16
|
+
hash |= 0; // Convert to 32bit integer
|
|
17
|
+
}
|
|
18
|
+
return hash;
|
|
19
|
+
}
|
|
20
|
+
export default hashCode;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useContext, useEffect } from "react";
|
|
2
|
+
import { ChartBlockContext } from "../components/DashboardPage/Block";
|
|
3
|
+
export const useBlockConfig = ({ title, dataExport }) => {
|
|
4
|
+
const blockContext = useContext(ChartBlockContext);
|
|
5
|
+
useEffect(() => blockContext?.setConfig({
|
|
6
|
+
title: title,
|
|
7
|
+
dataExport: dataExport
|
|
8
|
+
}), [title, dataExport]);
|
|
9
|
+
};
|