@geo2france/api-dashboard 1.15.0 → 1.17.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/Pie.js +1 -1
- package/dist/components/Charts/Statistics.js +3 -3
- package/dist/components/Charts/YearSerie.d.ts +13 -0
- package/dist/components/Charts/YearSerie.js +37 -9
- package/dist/components/Control/Control.d.ts +21 -5
- package/dist/components/Control/Control.js +28 -6
- package/dist/components/Control/Radio.d.ts +4 -4
- package/dist/components/Control/Radio.js +3 -0
- package/dist/components/Control/Select.d.ts +5 -3
- package/dist/components/Control/Select.js +4 -4
- package/dist/components/DashboardPage/Block.js +2 -1
- package/dist/components/DashboardPage/Page.d.ts +0 -21
- package/dist/components/DashboardPage/Page.js +27 -29
- package/dist/components/Dataset/Dataset.d.ts +11 -0
- package/dist/components/Dataset/Dataset.js +4 -5
- package/dist/components/Dataset/Producer.js +2 -2
- package/dist/components/Dataset/Transform.d.ts +13 -3
- package/dist/components/Dataset/Transform.js +5 -4
- package/dist/components/Dataset/context.d.ts +9 -0
- package/dist/components/Dataset/context.js +7 -0
- package/dist/components/Dataset/hooks.d.ts +14 -30
- package/dist/components/Dataset/hooks.js +46 -12
- package/dist/components/Layout/DashboardApp.js +5 -11
- package/dist/components/Layout/Footer.js +42 -4
- package/dist/components/Map/Map.js +0 -2
- package/dist/components/NextPrevSelect/NextPrevSelect.d.ts +7 -6
- package/dist/components/NextPrevSelect/NextPrevSelect.js +16 -12
- package/dist/dsl/index.d.ts +3 -2
- package/dist/dsl/index.js +3 -2
- package/dist/utils/usechartexports.js +1 -1
- package/package.json +1 -1
|
@@ -32,7 +32,7 @@ export const ChartPie = ({ dataset: dataset_id, nameKey, dataKey, unit, title, d
|
|
|
32
32
|
color: usePalette({ nColors: chart_data?.length }),
|
|
33
33
|
itemStyle: {
|
|
34
34
|
/* Use label's color if any, otherwise fallback to Echarts calculated color */
|
|
35
|
-
color: (p) =>
|
|
35
|
+
color: (p) => colors_labels.find(i => i.label.toLowerCase() === p.name.toLowerCase())?.color ?? colors?.[p.dataIndex] ?? '#000'
|
|
36
36
|
},
|
|
37
37
|
data: chart_data,
|
|
38
38
|
radius: donut ? ['40%', '75%'] : [0, '75%'],
|
|
@@ -25,12 +25,12 @@ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix
|
|
|
25
25
|
const dataset = useDataset(dataset_id);
|
|
26
26
|
const row = dataset?.data?.slice(-1)[0];
|
|
27
27
|
const value = row?.[dataKey]; // Dernière valeur du dataset. Caster en Number ?
|
|
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
|
+
const compare_value = compareWith === 'previous' ? dataset?.data?.slice(-2)?.[0]?.[dataKey] : dataset?.data?.slice(0, 1)?.[0]?.[dataKey]; //Première ou avant dernière
|
|
29
29
|
const evolution = relativeEvolution ? 100 * ((value - compare_value) / compare_value) : value - compare_value;
|
|
30
30
|
const evolution_unit = relativeEvolution ? '%' : unit;
|
|
31
31
|
const evolution_is_good = invertColor ? evolution < 0 : evolution > 0;
|
|
32
32
|
const tooltip = help && _jsx(Tooltip, { title: help, children: _jsx(QuestionCircleOutlined, {}) });
|
|
33
|
-
const CallbackParams = { value: value
|
|
33
|
+
const CallbackParams = { value: value ?? NaN, compareValue: compare_value ?? NaN, data: dataset?.data ?? [], row: row };
|
|
34
34
|
let subtitle;
|
|
35
35
|
if (annotation !== undefined) {
|
|
36
36
|
subtitle = typeof annotation === 'function' ? annotation(CallbackParams) : annotation;
|
|
@@ -69,5 +69,5 @@ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix
|
|
|
69
69
|
export const StatisticsCollection = ({ children, columns = 3, title }) => {
|
|
70
70
|
const arrayChildren = Children.toArray(children);
|
|
71
71
|
useBlockConfig({ title: title });
|
|
72
|
-
return (_jsx(Row, { gutter: [8, 8], children: arrayChildren.map((c, index) => (_jsx(Col, { xl: 24 / columns, xs: 24, children: c }, index))) }));
|
|
72
|
+
return (_jsx(Row, { gutter: [8, 8], style: { margin: 8 }, children: arrayChildren.map((c, index) => (_jsx(Col, { xl: 24 / columns, xs: 24, children: c }, index))) }));
|
|
73
73
|
};
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Graphique standard pour afficher des données annuelles
|
|
3
3
|
*/
|
|
4
|
+
import { EChartsOption, SeriesOption } from "echarts";
|
|
4
5
|
interface IYearSerieProps {
|
|
5
6
|
dataset: string;
|
|
6
7
|
title?: string;
|
|
7
8
|
yearKey: string;
|
|
8
9
|
valueKey: string;
|
|
10
|
+
secondaryValueKey?: string;
|
|
9
11
|
categoryKey?: string;
|
|
10
12
|
stack?: boolean;
|
|
11
13
|
yearMark?: number | string;
|
|
14
|
+
normalize?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Fonction de tri appliquée aux séries (SeriesOption) avant affichage.
|
|
17
|
+
* Passée directement à `Array.sort()`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Tri alphabétique des séries par leur nom
|
|
21
|
+
* seriesSort: (a, b) => a.name.localeCompare(b.name)
|
|
22
|
+
*/
|
|
23
|
+
seriesSort?: (a: SeriesOption, b: SeriesOption) => number;
|
|
12
24
|
type?: 'bar' | 'line' | 'area';
|
|
25
|
+
options?: Partial<EChartsOption>;
|
|
13
26
|
}
|
|
14
27
|
export declare const ChartYearSerie: React.FC<IYearSerieProps>;
|
|
15
28
|
export {};
|
|
@@ -7,7 +7,8 @@ import { useDataset } from "../Dataset/hooks";
|
|
|
7
7
|
import { usePalette, usePaletteLabels } from "../Palette/Palette";
|
|
8
8
|
import { ChartEcharts } from "./ChartEcharts";
|
|
9
9
|
import { useBlockConfig } from "../DashboardPage/Block";
|
|
10
|
-
|
|
10
|
+
import deepMerge from "../../utils/deepmerge";
|
|
11
|
+
export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, secondaryValueKey, yearKey, yearMark, stack: stack_input, title, type: chart_type = 'bar', normalize = false, seriesSort, options: custom_options = {} }) => {
|
|
11
12
|
const stack = stack_input || chart_type == 'line' ? false : true; // Pas de stack par défaut pour le type line
|
|
12
13
|
const dataset = useDataset(dataset_id);
|
|
13
14
|
const data = dataset?.data;
|
|
@@ -17,26 +18,49 @@ export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, yea
|
|
|
17
18
|
title: title,
|
|
18
19
|
dataExport: data
|
|
19
20
|
});
|
|
21
|
+
const rollupSpec = {
|
|
22
|
+
'value': op.sum(valueKey),
|
|
23
|
+
};
|
|
24
|
+
if (secondaryValueKey) {
|
|
25
|
+
rollupSpec['secondaryValue'] = op.sum(secondaryValueKey);
|
|
26
|
+
}
|
|
20
27
|
if (data && data.length > 0) {
|
|
21
28
|
const grouped_data = categoryKey ? from(data).groupby(yearKey, categoryKey) //Somme par année et categorykey
|
|
22
|
-
.rollup(
|
|
29
|
+
.rollup(rollupSpec)
|
|
23
30
|
.groupby(yearKey).orderby(yearKey)
|
|
24
31
|
:
|
|
25
32
|
from(data).groupby(yearKey) //Somme par année seulement
|
|
26
|
-
.rollup(
|
|
33
|
+
.rollup(rollupSpec)
|
|
34
|
+
.orderby(yearKey);
|
|
27
35
|
const all_years = from(data).groupby(yearKey).rollup({ [yearKey]: op.any(yearKey) });
|
|
28
36
|
const all_cats = categoryKey ? (from(data).groupby(categoryKey).rollup({ [categoryKey]: op.any(categoryKey) })) : from([{ 'cat': valueKey }]);
|
|
29
37
|
const full = all_years.cross(all_cats); // Contient chaque annee x catégorie (pour éviter les trous)
|
|
30
38
|
distinct_cat = all_cats.array(categoryKey || 'cat'); // Pour générer chaque serie
|
|
31
|
-
chart_data = full.join_left(grouped_data
|
|
39
|
+
chart_data = full.join_left(grouped_data
|
|
40
|
+
.derive({ part: d => 100 * d.value / op.sum(d.value) }) // Data for normalized view
|
|
41
|
+
.rename({ value: valueKey, part: `${valueKey}_pct`, secondaryValue: secondaryValueKey || '' }) // Rename to original var name
|
|
42
|
+
).objects();
|
|
43
|
+
chart_data.sort((a, b) => a[yearKey] - b[yearKey]);
|
|
32
44
|
}
|
|
33
45
|
const COLORS = usePalette({ nColors: distinct_cat?.length }) || [];
|
|
34
46
|
const colors_labels = usePaletteLabels();
|
|
35
47
|
const series = distinct_cat.map((cat, idx) => ({
|
|
36
48
|
name: cat,
|
|
37
49
|
type: chart_type === 'area' ? 'line' : chart_type,
|
|
38
|
-
data: categoryKey ? chart_data?.filter((row) => row[categoryKey] === cat)
|
|
39
|
-
|
|
50
|
+
data: categoryKey ? chart_data?.filter((row) => row[categoryKey] === cat)
|
|
51
|
+
.map((row) => ([String(row[yearKey]),
|
|
52
|
+
row[valueKey] || 0,
|
|
53
|
+
secondaryValueKey ? row[secondaryValueKey] : undefined,
|
|
54
|
+
row[`${valueKey}_pct`]
|
|
55
|
+
]))
|
|
56
|
+
: chart_data?.map((row) => ([String(row[yearKey]),
|
|
57
|
+
row[valueKey] || 0,
|
|
58
|
+
secondaryValueKey ? row[secondaryValueKey] : undefined,
|
|
59
|
+
row[`${valueKey}_pct`]])),
|
|
60
|
+
encode: {
|
|
61
|
+
x: 0, // annee
|
|
62
|
+
y: normalize ? 3 : 1 // valueKey ou `${ValueKey}_pct`
|
|
63
|
+
},
|
|
40
64
|
itemStyle: {
|
|
41
65
|
color: colors_labels.find(i => i.label.toLowerCase() === cat.toLowerCase())?.color ?? (COLORS && COLORS[idx % COLORS.length]),
|
|
42
66
|
},
|
|
@@ -44,11 +68,13 @@ export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, yea
|
|
|
44
68
|
areaStyle: chart_type === 'area' ? {} : undefined,
|
|
45
69
|
markLine: idx === 0 && yearMark ? {
|
|
46
70
|
symbol: 'none',
|
|
71
|
+
animation: false,
|
|
72
|
+
silent: true,
|
|
47
73
|
data: [
|
|
48
74
|
{ xAxis: String(yearMark) }
|
|
49
75
|
]
|
|
50
|
-
} : undefined
|
|
51
|
-
}));
|
|
76
|
+
} : undefined,
|
|
77
|
+
})).sort(seriesSort);
|
|
52
78
|
function tooltipFormatter(params) {
|
|
53
79
|
if (!params || params.length === 0)
|
|
54
80
|
return '';
|
|
@@ -74,7 +100,9 @@ export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, yea
|
|
|
74
100
|
},
|
|
75
101
|
yAxis: {
|
|
76
102
|
type: 'value',
|
|
103
|
+
max: normalize ? 100 : undefined,
|
|
104
|
+
min: normalize ? 0 : undefined,
|
|
77
105
|
},
|
|
78
106
|
};
|
|
79
|
-
return (_jsx(ChartEcharts, { option: option }));
|
|
107
|
+
return (_jsx(ChartEcharts, { notMerge: true, option: deepMerge({}, option, custom_options) }));
|
|
80
108
|
};
|
|
@@ -7,15 +7,31 @@ declare const Control: React.FC<IControlProps>;
|
|
|
7
7
|
export default Control;
|
|
8
8
|
export declare const useControl: (name: string) => string | undefined;
|
|
9
9
|
export declare const useAllControls: () => Record<string, any>;
|
|
10
|
-
export declare const list_to_options: (input?: string[] |
|
|
11
|
-
label: string
|
|
12
|
-
value: string
|
|
10
|
+
export declare const list_to_options: (input?: string[] | {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
13
|
}[]) => {
|
|
14
|
-
label: string
|
|
15
|
-
value: string
|
|
14
|
+
label: string;
|
|
15
|
+
value: string;
|
|
16
16
|
}[];
|
|
17
17
|
interface IControlProps {
|
|
18
18
|
children: ReactElement | ReactElement[];
|
|
19
19
|
}
|
|
20
20
|
export declare const DSL_Control: React.FC<IControlProps>;
|
|
21
21
|
export declare const ControlPreview: React.FC;
|
|
22
|
+
type ControlContextType = {
|
|
23
|
+
values: Record<string, any>;
|
|
24
|
+
register: (control: {
|
|
25
|
+
name: string;
|
|
26
|
+
value: any;
|
|
27
|
+
}) => void;
|
|
28
|
+
clear: () => void;
|
|
29
|
+
getAll: () => Record<string, any>;
|
|
30
|
+
};
|
|
31
|
+
export declare const ControlContext: React.Context<ControlContextType | undefined>;
|
|
32
|
+
export declare const CreateControlesRegistry: () => {
|
|
33
|
+
register: (c: Record<string, any>) => void;
|
|
34
|
+
clear: () => void;
|
|
35
|
+
getAll: () => Record<string, any>;
|
|
36
|
+
values: Record<string, any>;
|
|
37
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Descriptions, Form, Layout } from "antd";
|
|
3
|
-
import React, { useContext, useEffect } from "react";
|
|
4
|
-
import { ControlContext } from "../DashboardPage/Page";
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
|
|
5
4
|
const { Header } = Layout;
|
|
6
5
|
/*
|
|
7
6
|
* Composant destiné à recevoir un Form avec les contrôles de la page
|
|
@@ -28,7 +27,7 @@ export const useControl = (name) => {
|
|
|
28
27
|
if (!context_controls) {
|
|
29
28
|
throw new Error("useControl must be used within a ControlProvider");
|
|
30
29
|
}
|
|
31
|
-
const
|
|
30
|
+
const values = context_controls.getAll();
|
|
32
31
|
const value = values[name];
|
|
33
32
|
return value;
|
|
34
33
|
};
|
|
@@ -49,8 +48,8 @@ export const list_to_options = (input = []) => {
|
|
|
49
48
|
return [];
|
|
50
49
|
}
|
|
51
50
|
return input.map((o) => {
|
|
52
|
-
if (typeof o == "string"
|
|
53
|
-
return { label: String(o), value: o };
|
|
51
|
+
if (typeof o == "string") {
|
|
52
|
+
return { label: String(o), value: String(o) };
|
|
54
53
|
}
|
|
55
54
|
return o;
|
|
56
55
|
});
|
|
@@ -64,7 +63,7 @@ export const DSL_Control = ({ children }) => {
|
|
|
64
63
|
if (!context_controls) { //Le contexte peut être nul ?
|
|
65
64
|
throw new Error("useControl must be used within a ControlProvider");
|
|
66
65
|
}
|
|
67
|
-
const { values: _control,
|
|
66
|
+
const { values: _control, register: pushControl } = context_controls;
|
|
68
67
|
const childrenArray = React.Children.toArray(children).filter((child) => React.isValidElement(child));
|
|
69
68
|
//Ajout des nouvelles valeurs de controles dans le contexte de la page
|
|
70
69
|
const handleChange = (changed_value) => {
|
|
@@ -83,3 +82,26 @@ export const ControlPreview = ({}) => {
|
|
|
83
82
|
}));
|
|
84
83
|
return (_jsx(Descriptions, { items: items }));
|
|
85
84
|
};
|
|
85
|
+
export const ControlContext = createContext(undefined);
|
|
86
|
+
export const CreateControlesRegistry = () => {
|
|
87
|
+
/* CONTROLS */
|
|
88
|
+
const [controls, setControles] = useState({});
|
|
89
|
+
const pushControl = useCallback((c) => {
|
|
90
|
+
setControles(prev => ({
|
|
91
|
+
...prev,
|
|
92
|
+
...c
|
|
93
|
+
}));
|
|
94
|
+
}, []);
|
|
95
|
+
const clear = useCallback(() => {
|
|
96
|
+
setControles({});
|
|
97
|
+
}, []);
|
|
98
|
+
const getAll = useCallback(() => {
|
|
99
|
+
return controls;
|
|
100
|
+
}, [controls]);
|
|
101
|
+
return ({
|
|
102
|
+
register: pushControl,
|
|
103
|
+
clear,
|
|
104
|
+
getAll,
|
|
105
|
+
values: controls
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -2,18 +2,18 @@ import type { RadioGroupProps } from 'antd';
|
|
|
2
2
|
import { SimpleRecord } from '../../types';
|
|
3
3
|
export declare const buildOptionsFromData: (data: SimpleRecord[], labelField?: string, valueField?: string) => {
|
|
4
4
|
label: string;
|
|
5
|
-
value: string
|
|
5
|
+
value: string;
|
|
6
6
|
}[];
|
|
7
7
|
type ExtendedRadioGroupProps = RadioGroupProps & {
|
|
8
8
|
name?: string;
|
|
9
9
|
dataset?: string;
|
|
10
10
|
options?: {
|
|
11
11
|
label: string;
|
|
12
|
-
value: string
|
|
13
|
-
}[] | string[]
|
|
12
|
+
value: string;
|
|
13
|
+
}[] | string[];
|
|
14
14
|
labelField?: string;
|
|
15
15
|
valueField?: string;
|
|
16
|
-
initalValue?: string
|
|
16
|
+
initalValue?: string;
|
|
17
17
|
};
|
|
18
18
|
export declare const Radio: React.FC<ExtendedRadioGroupProps>;
|
|
19
19
|
export {};
|
|
@@ -6,6 +6,9 @@ import { list_to_options } from './Control';
|
|
|
6
6
|
// On construit les options depuis le tableau de données, utiliser pour Radio et Select
|
|
7
7
|
export const buildOptionsFromData = (data, labelField = 'label', valueField = 'value') => {
|
|
8
8
|
const t = from(data);
|
|
9
|
+
if (data.length <= 0) { //Avoir arquero error on empty data
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
9
12
|
return (t.select(labelField, valueField)
|
|
10
13
|
.dedupe(valueField)
|
|
11
14
|
.objects()
|
|
@@ -3,13 +3,15 @@ type ExtendedSelectProps = Omit<SelectProps<any>, 'options'> & {
|
|
|
3
3
|
dataset?: string;
|
|
4
4
|
options?: {
|
|
5
5
|
label: string;
|
|
6
|
-
value: string
|
|
7
|
-
}[] | string[]
|
|
8
|
-
initial_value?: string
|
|
6
|
+
value: string;
|
|
7
|
+
}[] | string[];
|
|
8
|
+
initial_value?: string;
|
|
9
9
|
labelField?: string;
|
|
10
10
|
valueField?: string;
|
|
11
11
|
name?: string;
|
|
12
|
+
label?: string;
|
|
12
13
|
arrows?: boolean;
|
|
14
|
+
reverse?: boolean;
|
|
13
15
|
};
|
|
14
16
|
export declare const Select: React.FC<ExtendedSelectProps>;
|
|
15
17
|
export {};
|
|
@@ -5,13 +5,13 @@ import NextPrevSelect from '../NextPrevSelect/NextPrevSelect';
|
|
|
5
5
|
import { list_to_options } from './Control';
|
|
6
6
|
// TODO : a fusionner avec NextPrevSelect pour n'avoir qu'un seul composant
|
|
7
7
|
// Actuellement, Select apporte seulement le fait de choisir les valeurs depuis un
|
|
8
|
-
export const Select = ({ name, dataset: datasetSource, options: input_options = [], labelField = 'label', valueField = 'value', arrows = false, initial_value: initial_value_in, ...rest }) => {
|
|
8
|
+
export const Select = ({ name, dataset: datasetSource, options: input_options = [], labelField = 'label', valueField = 'value', arrows = false, reverse = false, label, initial_value: initial_value_in, ...rest }) => {
|
|
9
9
|
const options = list_to_options(input_options);
|
|
10
10
|
const data = useDataset(datasetSource)?.data;
|
|
11
11
|
const data_options = datasetSource ? (data && buildOptionsFromData(data, labelField, valueField)) : options;
|
|
12
12
|
const myOptions = data_options && data_options.map((o) => {
|
|
13
|
-
if (typeof o == "string"
|
|
14
|
-
return { label: o, value: o };
|
|
13
|
+
if (typeof o == "string") {
|
|
14
|
+
return { label: o, value: String(o) };
|
|
15
15
|
}
|
|
16
16
|
return o;
|
|
17
17
|
});
|
|
@@ -22,5 +22,5 @@ export const Select = ({ name, dataset: datasetSource, options: input_options =
|
|
|
22
22
|
const value = initial_value == null || initial_value == false
|
|
23
23
|
? undefined
|
|
24
24
|
: initial_value;
|
|
25
|
-
return (_jsx(NextPrevSelect, { name: name, options: data_options, defaultValue: value, value: value, arrows: arrows, optionFilterProp: "label", ...rest }));
|
|
25
|
+
return (_jsx(NextPrevSelect, { name: name, label: label ?? name, options: data_options, defaultValue: value, value: value, arrows: arrows, reverse: reverse, optionFilterProp: "label", ...rest }));
|
|
26
26
|
};
|
|
@@ -5,6 +5,7 @@ import { Icon } from "@iconify/react";
|
|
|
5
5
|
import { ProducersFooter } from "../Dataset/Producer";
|
|
6
6
|
import { MoreOutlined } from '@ant-design/icons';
|
|
7
7
|
import { ErrorBoundary } from "../Layout/Error";
|
|
8
|
+
import { cardStyles } from "../../utils/cardStyles";
|
|
8
9
|
const { useToken } = theme;
|
|
9
10
|
export const ChartBlockContext = createContext(undefined);
|
|
10
11
|
export const DSL_ChartBlock = ({ children }) => {
|
|
@@ -37,7 +38,7 @@ export const DSL_ChartBlock = ({ children }) => {
|
|
|
37
38
|
};
|
|
38
39
|
DL();
|
|
39
40
|
};
|
|
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 })] }) }) }));
|
|
41
|
+
return (_jsx(ChartBlockContext.Provider, { value: { config: config, setConfig: (e) => setConfig(e) }, children: _jsx(Card, { className: "dashboard-element", style: { height: '100%' }, styles: cardStyles, extra: has_action && dropdown_toolbox, title: config.title, children: _jsxs(ErrorBoundary, { children: [children, _jsx(ProducersFooter, { component: children })] }) }) }));
|
|
41
42
|
};
|
|
42
43
|
export const useBlockConfig = ({ title, dataExport }) => {
|
|
43
44
|
const blockContext = useContext(ChartBlockContext);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { RowProps } from "antd";
|
|
2
2
|
import DashboardElement from "../DashboardElement/DashboardElement";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import { SimpleRecord } from "../../types";
|
|
5
4
|
import { Section } from "./Section";
|
|
6
5
|
type Section = {
|
|
7
6
|
key: string;
|
|
@@ -18,26 +17,6 @@ interface IDashboardPageProps {
|
|
|
18
17
|
}
|
|
19
18
|
declare const DashboardPage: React.FC<IDashboardPageProps>;
|
|
20
19
|
export default DashboardPage;
|
|
21
|
-
type dataset = {
|
|
22
|
-
id: string;
|
|
23
|
-
resource: string;
|
|
24
|
-
data?: SimpleRecord[];
|
|
25
|
-
isFetching: boolean;
|
|
26
|
-
isError: boolean;
|
|
27
|
-
producers?: any[];
|
|
28
|
-
geojson?: any;
|
|
29
|
-
dataHash?: number;
|
|
30
|
-
};
|
|
31
|
-
type ControlContextType = {
|
|
32
|
-
values: Record<string, any>;
|
|
33
|
-
pushValue: (control: {
|
|
34
|
-
name: string;
|
|
35
|
-
value: any;
|
|
36
|
-
}) => void;
|
|
37
|
-
};
|
|
38
|
-
export declare const DatasetContext: React.Context<Record<string, dataset>>;
|
|
39
|
-
export declare const DatasetRegistryContext: React.Context<(dataset: dataset) => void>;
|
|
40
|
-
export declare const ControlContext: React.Context<ControlContextType | undefined>;
|
|
41
20
|
interface IDSLDashboardPageProps {
|
|
42
21
|
children: React.ReactNode;
|
|
43
22
|
name?: string;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Button, Col, Dropdown, Flex, Grid, Layout, Radio, Row, Tabs, theme } from "antd";
|
|
3
|
-
import React, { isValidElement, useState,
|
|
3
|
+
import React, { isValidElement, useState, useEffect, useContext } from "react";
|
|
4
4
|
import { Helmet } from "react-helmet-async";
|
|
5
5
|
import { useSearchParamsState } from "../../utils/useSearchParamsState";
|
|
6
|
-
import Control, { DSL_Control } from "../Control/Control";
|
|
6
|
+
import Control, { ControlContext, DSL_Control } from "../Control/Control";
|
|
7
7
|
import { Dataset, Debug, Provider } from "../../dsl";
|
|
8
8
|
import { DEFAULT_PALETTE, Palette, PaletteContext } from "../Palette/Palette";
|
|
9
9
|
import { Section } from "./Section";
|
|
10
10
|
import { Icon } from "@iconify/react";
|
|
11
|
+
import { DatasetRegistryContext } from "../Dataset/context";
|
|
11
12
|
const { Header } = Layout;
|
|
12
13
|
const { useToken } = theme;
|
|
13
14
|
const getSection = (child) => React.isValidElement(child) ? child.props.section : undefined;
|
|
@@ -39,22 +40,19 @@ const DashboardPage = ({ children: children_input, control, row_gutter = [8, 8],
|
|
|
39
40
|
})), value: activeTab, onChange: (e) => setActiveTab(e.target.value) }), control] }) }), _jsx(Row, { gutter: row_gutter, style: { margin: 16 }, children: children.map((child, idx) => ({ child, idx })).filter(({ child }) => (getSection(child) ?? 'Autres') == activeTab).map(({ child, idx }) => _jsx(Col, { xl: 12, xs: 24, children: child }, idx)) })] }));
|
|
40
41
|
};
|
|
41
42
|
export default DashboardPage;
|
|
42
|
-
export const DatasetContext = createContext({});
|
|
43
|
-
export const DatasetRegistryContext = createContext(() => { }); // A modifier, utiliser un seul context
|
|
44
|
-
export const ControlContext = createContext(undefined);
|
|
45
43
|
export const DSL_DashboardPage = ({ name = 'Tableau de bord', columns = 2, children, debug = false }) => {
|
|
46
44
|
const { token } = useToken();
|
|
47
|
-
const [datasets, setdatasets] = useState({});
|
|
48
45
|
const [palette, setPalette] = useState(DEFAULT_PALETTE);
|
|
46
|
+
const datasetRegistry = useContext(DatasetRegistryContext);
|
|
47
|
+
const controlesRegistry = useContext(ControlContext);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
return () => {
|
|
50
|
+
datasetRegistry.clear();
|
|
51
|
+
controlesRegistry?.clear();
|
|
52
|
+
};
|
|
53
|
+
}, []);
|
|
49
54
|
//const allDatasetLoaded = Object.values(datasets).every(d => !d.isFetching);
|
|
50
55
|
//const isDatasetError = Object.values(datasets).some(d => d.isError);
|
|
51
|
-
// Ajouter ou mettre à jour un dataset
|
|
52
|
-
const pushDataset = (d) => {
|
|
53
|
-
setdatasets(prev => ({
|
|
54
|
-
...prev,
|
|
55
|
-
[d.id]: d
|
|
56
|
-
}));
|
|
57
|
-
};
|
|
58
56
|
const childrenArray = React.Children.toArray(children).filter(isValidElement);
|
|
59
57
|
const logicalComponents = [Dataset.name, Provider.name, Palette.name, Debug.name]; //Composant logiques, a ne pas mettre dans la grid
|
|
60
58
|
const getComponentKind = (c) => {
|
|
@@ -91,20 +89,20 @@ export const DSL_DashboardPage = ({ name = 'Tableau de bord', columns = 2, child
|
|
|
91
89
|
children: _jsx(Section, { title: 'Autres', children: visible_components })
|
|
92
90
|
});
|
|
93
91
|
}
|
|
94
|
-
return (_jsxs(_Fragment, { children: [_jsx(Helmet, { children: _jsx("title", { children: name }) }),
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
92
|
+
return (_jsxs(_Fragment, { children: [_jsx(Helmet, { children: _jsx("title", { children: name }) }), _jsxs(PaletteContext.Provider, { value: { palette, setPalette }, children: [control_components.length > 0 && _jsx(Header, { style: {
|
|
93
|
+
padding: 12,
|
|
94
|
+
position: "sticky",
|
|
95
|
+
top: 0,
|
|
96
|
+
zIndex: 600, // maplibre top zIndex if 500
|
|
97
|
+
backgroundColor: "#fff",
|
|
98
|
+
height: "auto",
|
|
99
|
+
width: "100%",
|
|
100
|
+
}, children: control_components }), items.length > 1 ?
|
|
101
|
+
_jsx(Tabs, { defaultActiveKey: "1", items: items, centered: true, tabBarStyle: { margin: 6,
|
|
102
|
+
padding: 4,
|
|
103
|
+
background: token.colorBgContainer,
|
|
104
|
+
borderRadius: token.borderRadiusLG }, style: { margin: 4 } })
|
|
105
|
+
:
|
|
106
|
+
_jsxs("div", { style: { margin: 4 }, children: [" ", items?.[0].children, " "] }) //Show content without tabs if only one
|
|
107
|
+
, logic_components] })] }));
|
|
110
108
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
+
import { SimpleRecord } from "../..";
|
|
2
3
|
import { CrudFilters, DataProvider } from "../../data_providers/types";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { ProviderType } from "./Provider";
|
|
@@ -13,5 +14,15 @@ interface IDatasetProps {
|
|
|
13
14
|
pageSize?: number;
|
|
14
15
|
meta?: any;
|
|
15
16
|
}
|
|
17
|
+
export type dataset = {
|
|
18
|
+
id: string;
|
|
19
|
+
resource: string;
|
|
20
|
+
data?: SimpleRecord[];
|
|
21
|
+
isFetching: boolean;
|
|
22
|
+
isError: boolean;
|
|
23
|
+
producers?: any[];
|
|
24
|
+
geojson?: any;
|
|
25
|
+
dataHash?: number;
|
|
26
|
+
};
|
|
16
27
|
export declare const DSL_Dataset: React.FC<IDatasetProps>;
|
|
17
28
|
export {};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useEffect } from "react";
|
|
3
3
|
import { useApi } from "../..";
|
|
4
|
-
import { ControlContext, DatasetRegistryContext } from "../DashboardPage/Page";
|
|
5
4
|
import { Producer } from "./Producer";
|
|
6
5
|
import React from "react";
|
|
7
|
-
import { Filter, Transform, useAllDatasets, useDatasets } from "../../dsl";
|
|
6
|
+
import { Filter, Transform, useAllControls, useAllDatasets, useDatasets } from "../../dsl";
|
|
8
7
|
import alasql from "alasql";
|
|
9
8
|
import { DataProviderContext, getProviderFromType } from "./Provider";
|
|
10
9
|
import { Join } from "./Join";
|
|
11
10
|
import { from } from "arquero";
|
|
12
11
|
import hashCode from "../../utils/hash_data";
|
|
12
|
+
import { DatasetRegistryContext } from "./context";
|
|
13
13
|
export const DSL_Dataset = ({ children, id, provider: provider_input, type: providerType = 'file', url: providerUrl, resource, pageSize, meta }) => {
|
|
14
14
|
const getTransformerFn = (component) => {
|
|
15
15
|
/*
|
|
@@ -57,8 +57,7 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
57
57
|
};
|
|
58
58
|
const datasetRegistryContext = useContext(DatasetRegistryContext);
|
|
59
59
|
const allDatasets = useAllDatasets();
|
|
60
|
-
const
|
|
61
|
-
const controls = controlContext?.values;
|
|
60
|
+
const controls = useAllControls();
|
|
62
61
|
const providerContext = useContext(DataProviderContext);
|
|
63
62
|
const provider = (providerUrl && getProviderFromType(providerType)(providerUrl)) || providerContext || provider_input;
|
|
64
63
|
if (provider === undefined) {
|
|
@@ -99,7 +98,7 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
99
98
|
useEffect(() => {
|
|
100
99
|
const finalData = data?.data && transformers.reduce((datat, fn) => fn(datat), data.data);
|
|
101
100
|
if (datasetRegistryContext) {
|
|
102
|
-
datasetRegistryContext({
|
|
101
|
+
datasetRegistryContext.register({
|
|
103
102
|
id: id,
|
|
104
103
|
resource: resource,
|
|
105
104
|
data: finalData,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useDatasets } from "./hooks";
|
|
3
3
|
import { Typography } from "antd";
|
|
4
|
-
const { Link } = Typography;
|
|
4
|
+
const { Link, Text } = Typography;
|
|
5
5
|
/**
|
|
6
6
|
* Les props sont récupérées par le parent (Dataset) et enregistrés avec le dataset
|
|
7
7
|
*/
|
|
@@ -21,7 +21,7 @@ export const ProducersFooter = ({ component }) => {
|
|
|
21
21
|
const uniqueProducers = Array.from(new Map(producers?.map(p => [p.nom, p]) // clé unique = nom
|
|
22
22
|
).values());
|
|
23
23
|
uniqueProducers.sort((a, b) => a.nom.localeCompare(b.nom));
|
|
24
|
-
return
|
|
24
|
+
return (producers?.length > 0) ? (_jsxs(Text, { type: "secondary", style: { padding: 4 }, children: ["Source des donn\u00E9es :", " ", uniqueProducers?.map((p, idx, arr) => (_jsxs("span", { children: [_jsx(Link, { href: p.url, children: p.nom }), idx < arr.length - 1 ? ', ' : ''] }, idx)))] })) : null;
|
|
25
25
|
}
|
|
26
26
|
return _jsx(_Fragment, {});
|
|
27
27
|
};
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { SimpleRecord } from "../..";
|
|
2
|
-
interface ITransformProps
|
|
3
|
-
|
|
2
|
+
interface ITransformProps {
|
|
3
|
+
/**
|
|
4
|
+
* Contenu du Transform :
|
|
5
|
+
* - **string** : SQL (interprété par Alasql)
|
|
6
|
+
* - **fonction** `(data: any) => SimpleRecord[]` : transforme les données brutes ou déjà transformées
|
|
7
|
+
* en un tableau de `SimpleRecord`.
|
|
8
|
+
*/
|
|
9
|
+
children: string | ((data: any) => SimpleRecord[]);
|
|
4
10
|
}
|
|
5
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Composant permettant d'appliquer une transformation à un jeu de données.
|
|
13
|
+
* La transformation est appliquée via une fonction ou une chaîne SQL.
|
|
14
|
+
*/
|
|
15
|
+
export declare const Transform: React.FC<ITransformProps>;
|
|
6
16
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Composant permettant d'appliquer une transformation à un jeu de données.
|
|
4
|
+
* La transformation est appliquée via une fonction ou une chaîne SQL.
|
|
5
|
+
*/
|
|
6
|
+
export const Transform = () => {
|
|
6
7
|
return _jsx(_Fragment, {});
|
|
7
8
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dataset } from "./Dataset";
|
|
2
|
+
interface DatasetRegistryContextValue {
|
|
3
|
+
register: (dataset: dataset) => void;
|
|
4
|
+
clear: () => void;
|
|
5
|
+
get: (name: string) => dataset | undefined;
|
|
6
|
+
getAll: () => Record<string, dataset>;
|
|
7
|
+
}
|
|
8
|
+
export declare const DatasetRegistryContext: import("react").Context<DatasetRegistryContextValue>;
|
|
9
|
+
export {};
|
|
@@ -1,30 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
isFetching: boolean;
|
|
16
|
-
isError: boolean;
|
|
17
|
-
producers?: any[];
|
|
18
|
-
geojson?: any;
|
|
19
|
-
dataHash?: number;
|
|
20
|
-
}[] | undefined;
|
|
21
|
-
export declare const useAllDatasets: () => {
|
|
22
|
-
id: string;
|
|
23
|
-
resource: string;
|
|
24
|
-
data?: import("../..").SimpleRecord[];
|
|
25
|
-
isFetching: boolean;
|
|
26
|
-
isError: boolean;
|
|
27
|
-
producers?: any[];
|
|
28
|
-
geojson?: any;
|
|
29
|
-
dataHash?: number;
|
|
30
|
-
}[] | undefined;
|
|
1
|
+
import { dataset } from "./Dataset";
|
|
2
|
+
export declare const useDataset: (dataset_id?: string) => dataset | undefined;
|
|
3
|
+
export declare const useAllDatasets: () => dataset[];
|
|
4
|
+
export declare const useDatasets: (dataset_ids?: string[]) => dataset[];
|
|
5
|
+
/**
|
|
6
|
+
* Fonction permettant de créer le registre de dataset
|
|
7
|
+
* avec les méthodes nécessaires.
|
|
8
|
+
*/
|
|
9
|
+
export declare const createDatasetRegistry: () => {
|
|
10
|
+
register: (d: dataset) => void;
|
|
11
|
+
clear: () => void;
|
|
12
|
+
get: (dataset_id?: string) => dataset | undefined;
|
|
13
|
+
getAll: () => Record<string, dataset>;
|
|
14
|
+
};
|
|
@@ -1,19 +1,53 @@
|
|
|
1
|
-
import { useContext } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import { useCallback, useContext, useState } from "react";
|
|
2
|
+
import { DatasetRegistryContext } from "./context";
|
|
3
|
+
// 🔹 Hook pour récupérer un dataset unique
|
|
3
4
|
export const useDataset = (dataset_id) => {
|
|
4
|
-
const
|
|
5
|
+
const datasetRegistry = useContext(DatasetRegistryContext);
|
|
5
6
|
if (dataset_id) {
|
|
6
|
-
return
|
|
7
|
+
return datasetRegistry.get(dataset_id);
|
|
7
8
|
}
|
|
8
|
-
// Retourne le premier dataset si pas d'id
|
|
9
|
-
const firstKey = Object.keys(datasetContext)[0];
|
|
10
|
-
return firstKey ? datasetContext[firstKey] : undefined;
|
|
11
9
|
};
|
|
10
|
+
// 🔹 Hook pour récupérer tous les datasets sous forme de tableau
|
|
11
|
+
export const useAllDatasets = () => {
|
|
12
|
+
const datasetRegistry = useContext(DatasetRegistryContext);
|
|
13
|
+
return Object.values(datasetRegistry.getAll());
|
|
14
|
+
};
|
|
15
|
+
// 🔹 Hook pour filtrer plusieurs datasets par id
|
|
12
16
|
export const useDatasets = (dataset_ids) => {
|
|
13
|
-
const
|
|
14
|
-
return (
|
|
17
|
+
const datasets = useAllDatasets();
|
|
18
|
+
return (datasets.filter(d => dataset_ids?.includes(d.id)));
|
|
15
19
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Fonction permettant de créer le registre de dataset
|
|
22
|
+
* avec les méthodes nécessaires.
|
|
23
|
+
*/
|
|
24
|
+
export const createDatasetRegistry = () => {
|
|
25
|
+
/* DATASET */
|
|
26
|
+
const [datasets, setdatasets] = useState({});
|
|
27
|
+
const pushDataset = useCallback((d) => {
|
|
28
|
+
setdatasets(prev => {
|
|
29
|
+
const existing = prev[d.id];
|
|
30
|
+
if (existing && existing.dataHash === d.dataHash && existing.isFetching === d.isFetching) { // Eviter les rerender si les données n'ont pas changé
|
|
31
|
+
return prev;
|
|
32
|
+
}
|
|
33
|
+
return { ...prev, [d.id]: d };
|
|
34
|
+
});
|
|
35
|
+
}, []);
|
|
36
|
+
const clearDatasets = useCallback(() => {
|
|
37
|
+
setdatasets({});
|
|
38
|
+
}, []);
|
|
39
|
+
const getDataset = useCallback((dataset_id) => {
|
|
40
|
+
if (!dataset_id)
|
|
41
|
+
return undefined;
|
|
42
|
+
return datasets[dataset_id] ?? undefined;
|
|
43
|
+
}, [datasets]);
|
|
44
|
+
const getAllDataset = useCallback(() => {
|
|
45
|
+
return datasets;
|
|
46
|
+
}, [datasets]);
|
|
47
|
+
return {
|
|
48
|
+
register: pushDataset,
|
|
49
|
+
clear: clearDatasets,
|
|
50
|
+
get: getDataset,
|
|
51
|
+
getAll: getAllDataset,
|
|
52
|
+
};
|
|
19
53
|
};
|
|
@@ -7,9 +7,11 @@ import DashboardSider from "./Sider";
|
|
|
7
7
|
import { Content } from "antd/es/layout/layout";
|
|
8
8
|
import { ErrorComponent } from "./Error";
|
|
9
9
|
import { DasbhoardFooter } from "./Footer";
|
|
10
|
-
import { createContext
|
|
11
|
-
import { ControlContext } from "../DashboardPage/Page";
|
|
10
|
+
import { createContext } from "react";
|
|
12
11
|
import { HelmetProvider } from "react-helmet-async";
|
|
12
|
+
import { createDatasetRegistry } from "../Dataset/hooks";
|
|
13
|
+
import { DatasetRegistryContext } from "../Dataset/context";
|
|
14
|
+
import { ControlContext, CreateControlesRegistry } from "../Control/Control";
|
|
13
15
|
//import '../../index.css' //TODO a intégrer en jsx
|
|
14
16
|
const queryClient = new QueryClient();
|
|
15
17
|
const default_theme = {
|
|
@@ -33,14 +35,6 @@ const default_theme = {
|
|
|
33
35
|
export const AppContext = createContext({});
|
|
34
36
|
const DashboardApp = ({ routes, theme, logo, brands, footerSlider, title, subtitle }) => {
|
|
35
37
|
const context_values = { title, subtitle, logo };
|
|
36
|
-
/*
|
|
37
|
-
const [controls, setControles] = useState({});
|
|
38
|
-
const pushControl = (c) => {
|
|
39
|
-
setControles(prev => ({
|
|
40
|
-
...prev,
|
|
41
|
-
...c
|
|
42
|
-
}));
|
|
43
|
-
};
|
|
44
|
-
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ConfigProvider, { theme: theme || default_theme /* Merger plutôt ?*/, children: _jsx(HelmetProvider, { children: _jsx(AppContext.Provider, { value: context_values, children: _jsx(ControlContext.Provider, { value: { values: controls, pushValue: pushControl }, children: _jsx(HashRouter, { children: _jsx(Routes, { children: _jsxs(Route, { element: _jsxs(Layout, { hasSider: true, style: { minHeight: '100vh' }, children: [_jsx(DashboardSider, { route_config: routes }), _jsxs(Layout, { children: [_jsx(Content, { style: { width: "100%" }, children: _jsx(Outlet, {}) }), _jsx(DasbhoardFooter, { brands: brands, slider: footerSlider })] })] }), children: [generateRoutes(routes), _jsx(Route, { path: "*", element: _jsx(ErrorComponent, {}) })] }) }) }) }) }) }) }) }));
|
|
38
|
+
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ConfigProvider, { theme: theme || default_theme /* Merger plutôt ?*/, children: _jsx(HelmetProvider, { children: _jsx(AppContext.Provider, { value: context_values, children: _jsx(DatasetRegistryContext.Provider, { value: createDatasetRegistry(), children: _jsx(ControlContext.Provider, { value: CreateControlesRegistry(), children: _jsx(HashRouter, { children: _jsx(Routes, { children: _jsxs(Route, { element: _jsxs(Layout, { hasSider: true, style: { minHeight: '100vh' }, children: [_jsx(DashboardSider, { route_config: routes }), _jsxs(Layout, { children: [_jsx(Content, { style: { width: "100%" }, children: _jsx(Outlet, {}) }), _jsx(DasbhoardFooter, { brands: brands, slider: footerSlider })] })] }), children: [generateRoutes(routes), _jsx(Route, { path: "*", element: _jsx(ErrorComponent, {}) })] }) }) }) }) }) }) }) }) }));
|
|
45
39
|
};
|
|
46
40
|
export default DashboardApp;
|
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Button, Layout, Typography } from "antd";
|
|
3
|
-
import { useContext, useState } from "react";
|
|
2
|
+
import { Button, Layout, theme, Typography } from "antd";
|
|
3
|
+
import { useContext, useEffect, useState } from "react";
|
|
4
4
|
import Slider from "@ant-design/react-slick";
|
|
5
5
|
import { UpOutlined, DownOutlined } from "@ant-design/icons";
|
|
6
6
|
import { AppContext } from "./DashboardApp";
|
|
7
7
|
import "slick-carousel/slick/slick.css";
|
|
8
8
|
import "slick-carousel/slick/slick-theme.css";
|
|
9
|
+
import { Icon } from "@iconify/react";
|
|
9
10
|
const { Text } = Typography;
|
|
11
|
+
const { useToken } = theme;
|
|
10
12
|
export const DasbhoardFooter = ({ brands, slider = true }) => {
|
|
11
13
|
const [isCollapsed, setIsCollapsed] = useState(window.innerWidth < 768 ? true : false);
|
|
14
|
+
const [showScrollIndicator, setShowScrollIndicator] = useState(true);
|
|
15
|
+
const { token } = useToken();
|
|
16
|
+
/* 🤖 IA Generated effect
|
|
17
|
+
* Permet d'afficher ou non le scrollIndicator
|
|
18
|
+
*/
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const checkShadow = () => {
|
|
21
|
+
const scrollTop = window.scrollY;
|
|
22
|
+
const windowHeight = window.innerHeight;
|
|
23
|
+
const docHeight = document.documentElement.scrollHeight;
|
|
24
|
+
setShowScrollIndicator(scrollTop + windowHeight < docHeight - 1);
|
|
25
|
+
};
|
|
26
|
+
// scroll listener
|
|
27
|
+
window.addEventListener("scroll", checkShadow);
|
|
28
|
+
// observer pour changements dynamiques du contenu
|
|
29
|
+
const observer = new ResizeObserver(checkShadow);
|
|
30
|
+
observer.observe(document.body);
|
|
31
|
+
checkShadow(); // initial
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener("scroll", checkShadow);
|
|
34
|
+
observer.disconnect();
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
12
37
|
const toggleCollapse = () => {
|
|
13
38
|
setIsCollapsed(!isCollapsed);
|
|
14
39
|
};
|
|
@@ -40,10 +65,23 @@ export const DasbhoardFooter = ({ brands, slider = true }) => {
|
|
|
40
65
|
height: "auto",
|
|
41
66
|
minHeight: "40px",
|
|
42
67
|
transition: "height 0.5s ease-in-out",
|
|
43
|
-
overflow: "
|
|
68
|
+
overflow: "visible",
|
|
44
69
|
borderTop: "1px solid #ccc",
|
|
45
70
|
zIndex: 600, // maplibre top zIndex if 500
|
|
46
|
-
}, children: [
|
|
71
|
+
}, children: [showScrollIndicator &&
|
|
72
|
+
/* Shaddow + chevron : show the user that remaing content is avaible downside */
|
|
73
|
+
_jsx("div", { className: "scroll-indicator", style: {
|
|
74
|
+
position: "absolute",
|
|
75
|
+
top: -40,
|
|
76
|
+
left: 0,
|
|
77
|
+
right: 0,
|
|
78
|
+
height: 40,
|
|
79
|
+
pointerEvents: "none",
|
|
80
|
+
display: "flex",
|
|
81
|
+
justifyContent: "center",
|
|
82
|
+
alignContent: "flex-end",
|
|
83
|
+
background: "linear-gradient(to bottom, rgba(255,255,255,0), rgba(0,0,0,0.1))",
|
|
84
|
+
}, children: _jsx(Icon, { icon: "fa6-solid:chevron-down", fontSize: 35, color: token.colorPrimary }) }), isCollapsed && (_jsxs(Text, { type: "secondary", children: [app_context?.title, " - ", app_context?.subtitle] })), _jsx("div", { style: { display: isCollapsed ? "none" : "block", padding: "10px 0" }, children: slider
|
|
47
85
|
// Logos avec défilement (choix par défaut)
|
|
48
86
|
? _jsx(Slider
|
|
49
87
|
// Défilement auto si plus de logos que la lagreur de l'écran ne peut en afficher
|
|
@@ -35,7 +35,6 @@ const build_geojson = (params) => {
|
|
|
35
35
|
else {
|
|
36
36
|
features_collection = undefined;
|
|
37
37
|
}
|
|
38
|
-
console.log(geomKey, features_collection);
|
|
39
38
|
return features_collection;
|
|
40
39
|
};
|
|
41
40
|
export const Map = ({ dataset, color, type, paint, categoryKey, popup = false, popupFormatter: popupFormatterUser, title, xKey, yKey }) => {
|
|
@@ -74,7 +73,6 @@ export const MapLayer = ({ dataset, categoryKey, color = 'red', type = 'circle',
|
|
|
74
73
|
// src (lib proj4 pour convertir)
|
|
75
74
|
const keys = data?.data?.[0] ? Object.keys(data?.data?.[0]) : undefined;
|
|
76
75
|
const geomKey = [geomKey_input, "geom", "geometry"].find(c => c && keys?.includes(c));
|
|
77
|
-
console.log(data?.id, data?.data, geomKey);
|
|
78
76
|
// Si x et y sont definie, on construit le geojson
|
|
79
77
|
const geojson = xKey && yKey && data?.data ?
|
|
80
78
|
build_geojson({ data: data.data, xKey: xKey, yKey: yKey })
|
|
@@ -2,16 +2,17 @@ import React from 'react';
|
|
|
2
2
|
import { CSSProperties } from "react";
|
|
3
3
|
type NextPrevSelectProps = {
|
|
4
4
|
options?: {
|
|
5
|
-
label: string
|
|
6
|
-
value: string
|
|
7
|
-
}[] | string[]
|
|
5
|
+
label: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}[] | string[];
|
|
8
8
|
style?: CSSProperties;
|
|
9
|
-
defaultValue?: string
|
|
10
|
-
value?: string
|
|
11
|
-
onChange?: (value: string
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
value?: string;
|
|
11
|
+
onChange?: (value: string) => void;
|
|
12
12
|
reverse?: boolean;
|
|
13
13
|
name?: string;
|
|
14
14
|
arrows?: boolean;
|
|
15
|
+
label?: string;
|
|
15
16
|
};
|
|
16
17
|
declare const NextPrevSelect: React.FC<NextPrevSelectProps>;
|
|
17
18
|
export default NextPrevSelect;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons";
|
|
3
|
-
import { Button, ConfigProvider, Flex, Form, Select } from "antd";
|
|
4
|
-
import { useEffect
|
|
3
|
+
import { Button, ConfigProvider, Flex, Form, Select, theme, Typography } from "antd";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
5
|
import { list_to_options } from '../Control/Control';
|
|
6
|
+
import { useSearchParamsState } from '../../utils/useSearchParamsState';
|
|
7
|
+
const { Text } = Typography;
|
|
6
8
|
// Update field and trigger form OnValueChange, thanks to : https://github.com/ant-design/ant-design/issues/23782#issuecomment-2114700558
|
|
7
9
|
const updateFieldValue = (form, name, value) => {
|
|
8
10
|
form.getInternalHooks('RC_FORM_INTERNAL_HOOKS').dispatch({
|
|
@@ -23,20 +25,22 @@ const style_button_right = {
|
|
|
23
25
|
borderBottomRightRadius: 4,
|
|
24
26
|
borderLeft: 0,
|
|
25
27
|
};
|
|
26
|
-
const NextPrevSelect = ({ name, options: input_options = [], style, value, defaultValue, onChange, reverse = false, arrows = true, ...rest }) => {
|
|
27
|
-
const [current_value, setCurrent_value] =
|
|
28
|
+
const NextPrevSelect = ({ name = 'sansnom', options: input_options = [], style, value, defaultValue, onChange, reverse = false, arrows = true, label, ...rest }) => {
|
|
29
|
+
const [current_value, setCurrent_value] = useSearchParamsState(name, String(defaultValue) || '');
|
|
30
|
+
const { token } = theme.useToken();
|
|
28
31
|
const form = Form.useFormInstance();
|
|
32
|
+
const labelApplied = label ?? name;
|
|
29
33
|
useEffect(() => {
|
|
30
|
-
|
|
31
|
-
}, [value]);
|
|
34
|
+
current_value && handleChange(current_value);
|
|
35
|
+
}, [value, current_value]);
|
|
32
36
|
const options = list_to_options(input_options);
|
|
33
37
|
const current_index = options?.findIndex((o) => o.value == form?.getFieldValue(name) || o.value == current_value);
|
|
34
|
-
const next = () => reverse
|
|
38
|
+
const next = () => String(reverse
|
|
35
39
|
? options[current_index - 1].value
|
|
36
|
-
: options[current_index + 1].value;
|
|
37
|
-
const previous = () => reverse
|
|
40
|
+
: options[current_index + 1].value);
|
|
41
|
+
const previous = () => String(reverse
|
|
38
42
|
? options[current_index + 1].value
|
|
39
|
-
: options[current_index - 1].value;
|
|
43
|
+
: options[current_index - 1].value);
|
|
40
44
|
const isFirst = () => reverse ? current_index == options.length - 1 : current_index == 0;
|
|
41
45
|
const isLast = () => reverse ? current_index == 0 : current_index == options.length - 1;
|
|
42
46
|
const handleChange = (v) => {
|
|
@@ -44,6 +48,6 @@ const NextPrevSelect = ({ name, options: input_options = [], style, value, defau
|
|
|
44
48
|
name && form && updateFieldValue(form, name, v);
|
|
45
49
|
onChange && onChange(v);
|
|
46
50
|
};
|
|
47
|
-
return (_jsxs(Flex, { style: style, name: name, children: [arrows && _jsx(Button, { style: style_button_left, onClick: () => handleChange(previous()), disabled: isFirst(), children: _jsx(CaretLeftOutlined, {}) }), _jsxs(ConfigProvider, { theme: arrows ? { components: { Select: { borderRadius: 0, }, }, } : undefined, children: [" ", _jsx(Form.Item, { name: name, label:
|
|
51
|
+
return (_jsxs(Flex, { style: style, align: 'center', name: name, children: [" ", arrows && _jsxs(Text, { type: 'secondary', style: { color: token.colorTextLabel }, children: [" ", labelApplied, "\u00A0:\u00A0"] }), arrows && _jsx(Button, { style: style_button_left, onClick: () => handleChange(previous()), disabled: isFirst(), children: _jsx(CaretLeftOutlined, {}) }), _jsxs(ConfigProvider, { theme: arrows ? { components: { Select: { borderRadius: 0, }, }, } : undefined, children: [" ", _jsx(Form.Item, { name: name, label: labelApplied, noStyle: arrows, initialValue: defaultValue, shouldUpdate: true, children: _jsx(Select, { className: "nextPrevSelect", options: options, style: { ...style }, value: current_value, defaultValue: defaultValue, onChange: handleChange, ...rest }) })] }), arrows && _jsx(Button, { style: style_button_right, onClick: () => handleChange(next()), disabled: isLast(), children: _jsx(CaretRightOutlined, {}) })] }));
|
|
48
52
|
};
|
|
49
53
|
export default NextPrevSelect;
|
package/dist/dsl/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DSL_DashboardPage as Dashboard } from "../components/DashboardPage/Page";
|
|
2
2
|
import { DSL_Dataset as Dataset } from "../components/Dataset/Dataset";
|
|
3
3
|
import { DSL_DataPreview as DataPreview } from "../components/Dataset/DataPreview";
|
|
4
|
-
import {
|
|
4
|
+
import { Transform } from "../components/Dataset/Transform";
|
|
5
5
|
import { DSL_Filter as Filter } from "../components/Dataset/Filter";
|
|
6
6
|
import { Provider } from "../components/Dataset/Provider";
|
|
7
7
|
import { useAllDatasets, useDataset, useDatasets } from "../components/Dataset/hooks";
|
|
@@ -20,4 +20,5 @@ import { useBlockConfig } from "../components/DashboardPage/Block";
|
|
|
20
20
|
import { Statistics, StatisticsCollection } from "../components/Charts/Statistics";
|
|
21
21
|
import { MapLayer, Map } from "../components/Map/Map";
|
|
22
22
|
import { Section } from "../components/DashboardPage/Section";
|
|
23
|
-
|
|
23
|
+
import { LegendControl } from "../components/MapLegend/MapLegend";
|
|
24
|
+
export { Dashboard, Dataset, Provider, Transform, Join, Filter, Section, DataPreview, ChartEcharts, ChartPie, ChartYearSerie, Statistics, StatisticsCollection, useDataset, useDatasets, useAllDatasets, useBlockConfig, Producer, Control, useControl, useAllControls, Radio, Select, Input, Palette, usePalette, usePaletteLabels, PalettePreview, Debug, Map, MapLayer, LegendControl };
|
package/dist/dsl/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DSL_DashboardPage as Dashboard } from "../components/DashboardPage/Page";
|
|
2
2
|
import { DSL_Dataset as Dataset } from "../components/Dataset/Dataset";
|
|
3
3
|
import { DSL_DataPreview as DataPreview } from "../components/Dataset/DataPreview";
|
|
4
|
-
import {
|
|
4
|
+
import { Transform } from "../components/Dataset/Transform";
|
|
5
5
|
import { DSL_Filter as Filter } from "../components/Dataset/Filter";
|
|
6
6
|
import { Provider } from "../components/Dataset/Provider";
|
|
7
7
|
import { useAllDatasets, useDataset, useDatasets } from "../components/Dataset/hooks";
|
|
@@ -20,4 +20,5 @@ import { useBlockConfig } from "../components/DashboardPage/Block";
|
|
|
20
20
|
import { Statistics, StatisticsCollection } from "../components/Charts/Statistics";
|
|
21
21
|
import { MapLayer, Map } from "../components/Map/Map";
|
|
22
22
|
import { Section } from "../components/DashboardPage/Section";
|
|
23
|
-
|
|
23
|
+
import { LegendControl } from "../components/MapLegend/MapLegend";
|
|
24
|
+
export { Dashboard, Dataset, Provider, Transform, Join, Filter, Section, DataPreview, ChartEcharts, ChartPie, ChartYearSerie, Statistics, StatisticsCollection, useDataset, useDatasets, useAllDatasets, useBlockConfig, Producer, Control, useControl, useAllControls, Radio, Select, Input, Palette, usePalette, usePaletteLabels, PalettePreview, Debug, Map, MapLayer, LegendControl };
|