@geo2france/api-dashboard 1.5.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.
Files changed (128) hide show
  1. package/README.MD +192 -0
  2. package/dist/components/Attributions/Attribution.test.d.ts +1 -0
  3. package/dist/components/Attributions/Attribution.test.js +19 -0
  4. package/dist/components/Attributions/Attributions.d.ts +17 -0
  5. package/dist/components/Attributions/Attributions.js +30 -0
  6. package/dist/components/Charts/ChartEcharts.d.ts +7 -0
  7. package/dist/components/Charts/ChartEcharts.js +35 -0
  8. package/dist/components/Charts/Pie.d.ts +10 -0
  9. package/dist/components/Charts/Pie.js +35 -0
  10. package/dist/components/Charts/YearSerie.d.ts +16 -0
  11. package/dist/components/Charts/YearSerie.js +70 -0
  12. package/dist/components/Control/Control.d.ts +21 -0
  13. package/dist/components/Control/Control.js +84 -0
  14. package/dist/components/Control/Radio.d.ts +19 -0
  15. package/dist/components/Control/Radio.js +27 -0
  16. package/dist/components/Control/Select.d.ts +15 -0
  17. package/dist/components/Control/Select.js +25 -0
  18. package/dist/components/DashboardChart/DashboardChart.d.ts +11 -0
  19. package/dist/components/DashboardChart/DashboardChart.js +42 -0
  20. package/dist/components/DashboardElement/DashboardElement.d.ts +21 -0
  21. package/dist/components/DashboardElement/DashboardElement.js +108 -0
  22. package/dist/components/DashboardElement/hooks.d.ts +28 -0
  23. package/dist/components/DashboardElement/hooks.js +29 -0
  24. package/dist/components/DashboardPage/Block.d.ts +15 -0
  25. package/dist/components/DashboardPage/Block.js +40 -0
  26. package/dist/components/DashboardPage/DashboardPage.test.d.ts +1 -0
  27. package/dist/components/DashboardPage/DashboardPage.test.js +40 -0
  28. package/dist/components/DashboardPage/Page.d.ts +42 -0
  29. package/dist/components/DashboardPage/Page.js +80 -0
  30. package/dist/components/Dataset/DataPreview.d.ts +12 -0
  31. package/dist/components/Dataset/DataPreview.js +21 -0
  32. package/dist/components/Dataset/Dataset.d.ts +17 -0
  33. package/dist/components/Dataset/Dataset.js +94 -0
  34. package/dist/components/Dataset/Filter.d.ts +9 -0
  35. package/dist/components/Dataset/Filter.js +7 -0
  36. package/dist/components/Dataset/Join.d.ts +8 -0
  37. package/dist/components/Dataset/Join.js +7 -0
  38. package/dist/components/Dataset/Producer.d.ts +18 -0
  39. package/dist/components/Dataset/Producer.js +27 -0
  40. package/dist/components/Dataset/Provider.d.ts +21 -0
  41. package/dist/components/Dataset/Provider.js +22 -0
  42. package/dist/components/Dataset/Transform.d.ts +6 -0
  43. package/dist/components/Dataset/Transform.js +7 -0
  44. package/dist/components/Dataset/hooks.d.ts +24 -0
  45. package/dist/components/Dataset/hooks.js +19 -0
  46. package/dist/components/Debug/Debug.d.ts +1 -0
  47. package/dist/components/Debug/Debug.js +24 -0
  48. package/dist/components/FlipCard/FlipCard.d.ts +12 -0
  49. package/dist/components/FlipCard/FlipCard.js +38 -0
  50. package/dist/components/FlipCard/FlipCard.test.d.ts +1 -0
  51. package/dist/components/FlipCard/FlipCard.test.js +36 -0
  52. package/dist/components/KeyFigure/KeyFigure.d.ts +18 -0
  53. package/dist/components/KeyFigure/KeyFigure.js +13 -0
  54. package/dist/components/Layout/DashboardApp.d.ts +18 -0
  55. package/dist/components/Layout/DashboardApp.js +46 -0
  56. package/dist/components/Layout/Error.d.ts +2 -0
  57. package/dist/components/Layout/Error.js +6 -0
  58. package/dist/components/Layout/Footer.d.ts +6 -0
  59. package/dist/components/Layout/Footer.js +47 -0
  60. package/dist/components/Layout/Sider.d.ts +9 -0
  61. package/dist/components/Layout/Sider.js +48 -0
  62. package/dist/components/LoadingContainer/LoadingContainer.d.ts +17 -0
  63. package/dist/components/LoadingContainer/LoadingContainer.js +33 -0
  64. package/dist/components/MapLegend/MapLegend.d.ts +12 -0
  65. package/dist/components/MapLegend/MapLegend.js +19 -0
  66. package/dist/components/NextPrevSelect/NextPrevSelect.d.ts +17 -0
  67. package/dist/components/NextPrevSelect/NextPrevSelect.js +49 -0
  68. package/dist/components/Palette/Palette.d.ts +18 -0
  69. package/dist/components/Palette/Palette.js +29 -0
  70. package/dist/data_providers/datafair/datafair.test.d.ts +1 -0
  71. package/dist/data_providers/datafair/datafair.test.js +39 -0
  72. package/dist/data_providers/datafair/index.d.ts +15 -0
  73. package/dist/data_providers/datafair/index.js +33 -0
  74. package/dist/data_providers/datafair/utils/axios.d.ts +2 -0
  75. package/dist/data_providers/datafair/utils/axios.js +13 -0
  76. package/dist/data_providers/datafair/utils/generateFilter.d.ts +1 -0
  77. package/dist/data_providers/datafair/utils/generateFilter.js +40 -0
  78. package/dist/data_providers/datafair/utils/generateSort.d.ts +1 -0
  79. package/dist/data_providers/datafair/utils/generateSort.js +19 -0
  80. package/dist/data_providers/datafair/utils/index.d.ts +4 -0
  81. package/dist/data_providers/datafair/utils/index.js +4 -0
  82. package/dist/data_providers/datafair/utils/mapOperator.d.ts +1 -0
  83. package/dist/data_providers/datafair/utils/mapOperator.js +13 -0
  84. package/dist/data_providers/file/index.d.ts +6 -0
  85. package/dist/data_providers/file/index.js +25 -0
  86. package/dist/data_providers/file/utils/axios.d.ts +2 -0
  87. package/dist/data_providers/file/utils/axios.js +26 -0
  88. package/dist/data_providers/types.d.ts +52 -0
  89. package/dist/data_providers/types.js +2 -0
  90. package/dist/data_providers/wfs/index.d.ts +3 -0
  91. package/dist/data_providers/wfs/index.js +43 -0
  92. package/dist/data_providers/wfs/utils/axios.d.ts +2 -0
  93. package/dist/data_providers/wfs/utils/axios.js +26 -0
  94. package/dist/data_providers/wfs/utils/generateFilter.d.ts +4 -0
  95. package/dist/data_providers/wfs/utils/generateFilter.js +45 -0
  96. package/dist/data_providers/wfs/utils/generateSort.d.ts +1 -0
  97. package/dist/data_providers/wfs/utils/generateSort.js +20 -0
  98. package/dist/data_providers/wfs/utils/index.d.ts +4 -0
  99. package/dist/data_providers/wfs/utils/index.js +4 -0
  100. package/dist/data_providers/wfs/utils/mapOperator.d.ts +1 -0
  101. package/dist/data_providers/wfs/utils/mapOperator.js +36 -0
  102. package/dist/data_providers/wfs/wfs.test.d.ts +1 -0
  103. package/dist/data_providers/wfs/wfs.test.js +60 -0
  104. package/dist/dsl/index.d.ts +19 -0
  105. package/dist/dsl/index.js +19 -0
  106. package/dist/index.d.ts +29 -0
  107. package/dist/index.js +33 -0
  108. package/dist/types.d.ts +15 -0
  109. package/dist/types.js +1 -0
  110. package/dist/utils/baserecordtogeojsonpoint.d.ts +13 -0
  111. package/dist/utils/baserecordtogeojsonpoint.js +17 -0
  112. package/dist/utils/cardStyles.d.ts +2 -0
  113. package/dist/utils/cardStyles.js +12 -0
  114. package/dist/utils/deepmerge.d.ts +2 -0
  115. package/dist/utils/deepmerge.js +24 -0
  116. package/dist/utils/route_utils.d.ts +6 -0
  117. package/dist/utils/route_utils.js +14 -0
  118. package/dist/utils/useApi.d.ts +12 -0
  119. package/dist/utils/useApi.js +14 -0
  120. package/dist/utils/useMapControl.d.ts +8 -0
  121. package/dist/utils/useMapControl.js +24 -0
  122. package/dist/utils/useSearchParamsState.d.ts +11 -0
  123. package/dist/utils/useSearchParamsState.js +18 -0
  124. package/dist/utils/usechartexports.d.ts +17 -0
  125. package/dist/utils/usechartexports.js +39 -0
  126. package/dist/utils/usecharthightlight.d.ts +25 -0
  127. package/dist/utils/usecharthightlight.js +44 -0
  128. package/package.json +98 -0
package/README.MD ADDED
@@ -0,0 +1,192 @@
1
+ # API-Dashboard
2
+
3
+ Collection de composants React pour faciliter la création de **tableaux de bords territoriaux**.
4
+
5
+ Le projet permet la mise en place d'un tableau de bord facile à déployer sur une **infrastucture légère**.
6
+ Le tableau de bord, une fois compilé, peut-être mis à disposition des utilisateurs via un **simple server web** (HTTP/HTTPS) sans configuration particulière.
7
+ Il s'agit d'une application React (Javascript) s'executant dans le navigateur des utilisateurs. Il n'y a pas **backend à installer**,
8
+ l'application récupère les données via API **auprès d'un partenaire** (plateforme régionale, portail open-data, etc.) ou sur **votre serveur de données**.
9
+ Les données sont ensuites traitées par le client et présentées à l'utilisateur via des graphiques ou cartes.
10
+
11
+ Si des données sensibles alimentent un tableau de bord, l'authentification et la sécurité sont gérées au niveau du serveur de données. C'est donc
12
+ lui qui va s'assurer que l'utilisateur a un droit d'accès aux données. L'application peut donc tout être utilisée pour présenter
13
+ des données sensibles ou même des graphiques dont les données diffèrent selon les droits de l'utilisateur.
14
+
15
+ Les API suivantes sont actuellement supportées (interrogation, filtre, pagination, etc. ) :
16
+ - [WFS](src/data_providers/wfs/) : API proposée par la plupart des serveurs geographiques (QGIS Server, GeoServer, ArcGIS server, etc.)
17
+ - [Data Fair](src/data_providers/datafair/) : API de la solution open source Data Fair.
18
+ - En développement : OGC API Features, TJS
19
+
20
+ ![diag](architecture_1.png)
21
+
22
+ En bref :
23
+
24
+ - ✅ Déploiement facile et rapide (_client side_)
25
+ - ✅ Présentez au même endroit des données tierces, vos données et celles de vos partenaires
26
+ - ✅ Possibilité de visualiser des données sensibles
27
+ - ✅ Flexibilité
28
+
29
+ Les composants sont actuellement utilisés pour le [tableau de bord de l'Odema](https://github.com/geo2france/odema-dashboard).
30
+
31
+ ## Installation
32
+
33
+ `npm install https://github.com/geo2france/api-dashboard/releases/latest/download/api-dashboard.tgz`
34
+
35
+
36
+ ## Utilisation
37
+
38
+ Le tableau de bord est construit de manière déclarative **JSX** (accronyme de JavaScript XML).
39
+ Comme son nom l'indique, il permet de combiner la clarté et l'efficacité du XML, avec la souplesse et la puissance du JavaScript.
40
+
41
+ La construction du tableau de bord repose sur l'articulation de 3 grands concepts :
42
+
43
+ - Les `<Dataset>` permettent de rappatrier, filter et traiter laes données depuis des sources distantes : [documentation](./src/components/Dataset/README.md)
44
+ - Les graphiques `<ChartXXX>` et `<MapXXX>` constituent la partie essentielle du tableau de bord
45
+ - Les `<Control>` pour que les utilisateurs puissent interragir avec le tableau de bord (filter, activer/désactiver une option, etc.) : [documentation](./src/components/Control/README.md)
46
+
47
+ Le JSX permet aussi d'ajouter des éléments dynamiques en Javascript.
48
+
49
+ ```jsx
50
+
51
+ import { Transform, Dashboard, Dataset, Filter, Producer, Control, ChartPie, useControl } from "api-dashboard/dsl"
52
+
53
+ export const MaPremierePage = () => (
54
+
55
+ <Dashboard>
56
+
57
+ <Dataset
58
+ id="dma_collecte_traitement"
59
+ resource="sinoe-(r)-destination-des-dma-collectes-par-type-de-traitement/lines"
60
+ url="https://data.ademe.fr/data-fair/api/v1/datasets"
61
+ type="datafair"
62
+ pageSize={5000}>
63
+ <Filter field="L_REGION">Hauts-de-France</Filter>
64
+ {/* Un filtre statique appliqué à l'API qui fournie les données */}
65
+
66
+ <Filter field="ANNEE">{useControl('annee')}</Filter>
67
+ {/* Un second filtre : l'année choisie par l'utilisateur */}
68
+
69
+ <Transform>SELECT [ANNEE], [L_TYP_REG_DECHET], SUM([TONNAGE_DMA]) as [TONNAGE_DMA] FROM ? GROUP BY [ANNEE], [L_TYP_REG_DECHET]</Transform>
70
+ {/* Transformation local des données */}
71
+
72
+ <Producer url="https://odema-hautsdefrance.org/">Odema</Producer>
73
+ {/* Pour créditer les graphiques */}
74
+ </Dataset>
75
+
76
+ <Control>
77
+ <Select name="annee" options={[2021,2019,2017]} initial_value={2019} arrows={true} />
78
+ </Control>
79
+ {/* Un control permettant à mon utilisateur de choisir l'année */}
80
+
81
+ <ChartPie title={`Tonnages de déchets en ${useControl('annee')}`} dataset='dma_collecte_traitement' nameKey='L_TYP_REG_DECHET' dataKey='TONNAGE_DMA' />
82
+ {/* Un graphique camembert standard. J'indique mon jeu de données et les colonnes à utiliser */}
83
+
84
+ </Dashboard>
85
+ )
86
+ ```
87
+
88
+ ## Bien débuter
89
+
90
+ 1. Mise en place du projet template (_en cours_)
91
+ 2. Créer une nouvelle page
92
+ 3. Ajouter des [jeux de données](./src/components/Dataset/README.md)
93
+ 4. Ajouter un [graphique](./src/components/Charts/README.md)
94
+ 5. Personnaliser son tableau : ajouter une [Palette](./src/components/Palette/README.md), configurer le theme [theme AntDesign](https://ant.design/docs/react/customize-theme#customize-design-token) (_TODO_)
95
+ 6. Ajouter de l'interactivité : les [contrôles utilisateur](./src/components/Control/README.md)
96
+
97
+
98
+ ## Aller plus loin (développeur)
99
+
100
+ ### Développement de composants dataviz
101
+
102
+ La bibliothèque propose des graphiques de bases, mais il possible de développer ses propres composants React.
103
+ Des fonctions sont proposés afin d'aider le développeur dans cette tâche : accéder facilement aux données ou aux options utilisateurs, gestion des erreurs, etc.
104
+
105
+ Le développeur est libre d'utiliser bibliothèque dataviz ou carto de son choix, à partir du moment où le composant retourne un élément visuelle.
106
+ Nous préconisons [Echarts](https://echarts.apache.org), mais d'autres sont utilisables ([Recharts](https://recharts.org/), [Chart.js](https://www.chartjs.org/), etc.)
107
+
108
+ `TODO : guide développement composant`
109
+
110
+ ### Développement de contrôles
111
+
112
+ Il est également possible de développer des contrôle utilisateurs (éléments de formulaires) personalisés.
113
+
114
+ `TODO : guide développement control. Le composant doit retourner un <Form.Item />`
115
+
116
+
117
+
118
+
119
+ ## Documentation (ancienne)
120
+
121
+ ⭐ Essentiel
122
+ 👨‍💻 Utilisateur avancé
123
+
124
+ ### Mise en page et structure
125
+
126
+ - [DashboardApp](/src/components/Layout/) ⭐
127
+ - [DashboardPage](/src/components/DashboardPage/) ⭐
128
+ - [DashboardElement](/src/components/DashboardElement/) ⭐
129
+
130
+
131
+ ### Composants
132
+
133
+ - [KeyFigure](/src/components/KeyFigure/) ⭐
134
+ - [NextPrevSelect](/src/components/NextPrevSelect/) ⭐
135
+ - [MapLegend](/src/components/MapLegend/) ⭐
136
+ - [FlipCard](/src/components/FlipCard/)
137
+ - [LoadingContainer](/src/components/LoadingContainer/)
138
+
139
+ ### Hooks et fonctions
140
+
141
+ - [useApi](/src/utils/README.MD) ⭐
142
+ - [useSearchParamsState](/src/utils/README.MD) ⭐
143
+ - [useChartEvents](/src/utils/README.MD) 👨‍💻
144
+ - [useChartActionHightlight](/src/utils/README.MD) 👨‍💻
145
+ - [useMapControl](/src/utils/README.MD) 👨‍💻
146
+ - [useChartExport](/src/utils/README.MD)
147
+
148
+ ### Fournisseur de données
149
+
150
+ - [WFS](/src/data_providers/wfs/) ⭐
151
+ - [Datafair](/src/data_providers/datafair/) ⭐
152
+ - [Filte](/src/data_providers/file/) ⭐
153
+
154
+ ![block-graph](block-graph.png)
155
+
156
+ <!---
157
+ ```mermaid
158
+ graph TD;
159
+
160
+ subgraph "&lt;DashboardApp&gt;"
161
+ subgraph "&lt;DashboardPage&gt;"
162
+ subgraph "&lt;DashboardElement&gt;"
163
+ subgraph "Chart(Echart)"
164
+ end
165
+ end
166
+ subgraph "&lt;DashboardElement&gt;"
167
+ subgraph "Chart(Echart)"
168
+ end
169
+ end
170
+ subgraph "&lt;DashboardElement&gt;"
171
+ subgraph "Chart(Echart)"
172
+ end
173
+ end
174
+ end
175
+
176
+ subgraph "&lt;DashboardPage&gt;"
177
+ subgraph "&lt;DashboardElement&gt;"
178
+ subgraph "Chart(Echart)"
179
+ end
180
+ end
181
+ subgraph "&lt;DashboardElement&gt;"
182
+ subgraph "Chart(Echart)"
183
+ end
184
+ end
185
+ subgraph "&lt;DashboardElement&gt;"
186
+ subgraph "Chart(Echart)"
187
+ end
188
+ end
189
+ end
190
+ end
191
+ ```
192
+ --->
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import Attribution from './Attributions';
5
+ describe("Attribution Component", () => {
6
+ test("initial state", async () => {
7
+ // Génération DOM virtuel et sélection des noeuds
8
+ render(_jsx(Attribution, { licenses: ['CC', 'SA'], data: [{ url: "http://monurl.fr", name: "MyDataSourceName" }] }));
9
+ // Assertions
10
+ expect(screen.getByText("MyDataSourceName")).toBeInTheDocument();
11
+ expect(screen.getByText("Source des données:")).toBeInTheDocument();
12
+ const linkElement = screen.getByText("MyDataSourceName");
13
+ expect(linkElement).toHaveAttribute('href', "http://monurl.fr");
14
+ expect(screen.queryByLabelText('CC')).toBeInTheDocument();
15
+ expect(screen.queryByLabelText('SA')).toBeInTheDocument();
16
+ expect(screen.queryByLabelText('NC')).not.toBeInTheDocument();
17
+ expect(screen.queryByLabelText('BY')).not.toBeInTheDocument();
18
+ });
19
+ });
@@ -0,0 +1,17 @@
1
+ import React, { CSSProperties } from 'react';
2
+ import { License } from '../../types';
3
+ export interface SourceProps {
4
+ name: string;
5
+ url?: string;
6
+ }
7
+ export interface SourceMakerProps {
8
+ maker?: SourceProps;
9
+ sources?: SourceProps[];
10
+ }
11
+ interface AttributionProps {
12
+ data: SourceMakerProps | SourceProps[];
13
+ style?: CSSProperties;
14
+ licenses?: License[];
15
+ }
16
+ declare const Attribution: React.FC<AttributionProps>;
17
+ export default Attribution;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Tooltip, Typography } from 'antd';
3
+ import { FaCreativeCommons, FaCreativeCommonsBy, FaCreativeCommonsNc, FaCreativeCommonsPd, FaCreativeCommonsSa, FaCreativeCommonsZero } from 'react-icons/fa';
4
+ const { Text, Link } = Typography;
5
+ const LogoLicence = ({ license, style }) => {
6
+ switch (license) {
7
+ case "CC":
8
+ return _jsx(FaCreativeCommons, { style: style });
9
+ case "BY":
10
+ return _jsx(FaCreativeCommonsBy, { style: style });
11
+ case "NC":
12
+ return _jsx(FaCreativeCommonsNc, { style: style });
13
+ case "PD":
14
+ return _jsx(FaCreativeCommonsPd, { style: style });
15
+ case "SA":
16
+ return _jsx(FaCreativeCommonsSa, { style: style });
17
+ case "ZERO":
18
+ return _jsx(FaCreativeCommonsZero, { style: style });
19
+ }
20
+ };
21
+ const Attribution = ({ data, style, licenses }) => {
22
+ const licence_logo_style = { height: '12px', width: '12px' };
23
+ const sources = Array.isArray(data) ? data : data.sources;
24
+ const maker = Array.isArray(data) ? undefined : data.maker;
25
+ const plural = (sources?.length ?? 0) > 1 ? 's' : '';
26
+ return (_jsx("div", { style: { paddingLeft: 4, paddingBottom: 4, ...style }, children: _jsxs(Text, { type: "secondary", children: [`Source${plural} des données : `, sources?.map((e, i) => (_jsxs("span", { children: [_jsx(Link, { href: e.url, children: e.name }), i < sources.length - 1 ? ", " : ""] }, i))), maker && _jsxs("span", { children: [' ', "| R\u00E9alisation : ", _jsx(Link, { href: maker.url, children: maker.name })] }), _jsx("span", { style: { marginLeft: 5 }, children: _jsx(Tooltip, { title: licenses?.join(' '), placement: "bottom", children: licenses?.map((license, index) => {
27
+ return (_jsx("span", { style: { marginLeft: 2 }, children: _jsx(LogoLicence, { license: license, style: licence_logo_style, "aria-label": license }) }, index));
28
+ }) }) })] }) }));
29
+ };
30
+ export default Attribution;
@@ -0,0 +1,7 @@
1
+ import { EChartsOption } from "echarts";
2
+ import EChartsReact from "echarts-for-react";
3
+ interface ChartEchartsProps {
4
+ option?: EChartsOption;
5
+ }
6
+ export declare const ChartEcharts: import("react").ForwardRefExoticComponent<ChartEchartsProps & import("react").RefAttributes<EChartsReact>>;
7
+ export {};
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useImperativeHandle, useRef } from 'react';
3
+ import { theme } from 'antd';
4
+ import EChartsReact from "echarts-for-react";
5
+ import { usePalette } from "../Palette/Palette";
6
+ import deepMerge from "../../utils/deepmerge";
7
+ const { useToken } = theme;
8
+ /*
9
+ * Ce composant peut servir de base aux développements d'autres composants ou être utilisé directement dans une page (non conseillé).
10
+ * - Applique la palette utilisateur
11
+ * - Utilise le style de texte de l'application
12
+ * devnote : A partir de React 19, ne plus utiliser forwardRef https://react.dev/reference/react/forwardRef
13
+ */
14
+ export const ChartEcharts = forwardRef(({ option = {} }, ref) => {
15
+ const innerRef = useRef(null);
16
+ useImperativeHandle(ref, () => innerRef.current, []); // Pour exposer le innerref au parent
17
+ const { token } = useToken();
18
+ const n_series = Array.isArray(option.series) ? option.series?.length : 1;
19
+ // Récupérer et traduire les éléments depuis le theme antdesign : police, couleurs, etc..
20
+ // Ceci peut-être surchargé par les options de l'utilisateur
21
+ const default_option = {
22
+ color: usePalette({ nColors: n_series }),
23
+ textStyle: {
24
+ fontFamily: token.fontFamily,
25
+ color: token.colorTextSecondary
26
+ },
27
+ xAxis: {
28
+ axisLine: { lineStyle: { color: token.colorTextSecondary } },
29
+ },
30
+ yAxis: {
31
+ axisLine: { lineStyle: { color: token.colorTextSecondary } },
32
+ },
33
+ };
34
+ return (_jsx(EChartsReact, { option: deepMerge({}, default_option, option), ref: innerRef }));
35
+ });
@@ -0,0 +1,10 @@
1
+ interface IChartPieProps {
2
+ dataset?: string;
3
+ dataKey: string;
4
+ nameKey: string;
5
+ unit?: string;
6
+ title?: string;
7
+ donut?: boolean;
8
+ }
9
+ export declare const ChartPie: React.FC<IChartPieProps>;
10
+ export {};
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useEffect } from "react";
3
+ import { useDataset } from "../Dataset/hooks";
4
+ import { ChartBlockContext } from "../DashboardPage/Block";
5
+ import { usePalette } from "../Palette/Palette";
6
+ import { from, op } from "arquero";
7
+ import { ChartEcharts } from "./ChartEcharts";
8
+ export const ChartPie = ({ dataset: dataset_id, nameKey, dataKey, unit, title, donut = false }) => {
9
+ const dataset = useDataset(dataset_id);
10
+ const blockConfig = useContext(ChartBlockContext);
11
+ const data = dataset?.data;
12
+ const block_config = {
13
+ title: title,
14
+ dataExport: data
15
+ };
16
+ useEffect(() => blockConfig?.setConfig(block_config), [data]);
17
+ const chart_data = data && from(data).groupby(nameKey).rollup({ value: op.sum(dataKey) }).objects().map((d) => ({
18
+ name: d[nameKey],
19
+ value: d.value,
20
+ }));
21
+ const option = {
22
+ color: usePalette({ nColors: chart_data?.length }),
23
+ xAxis: { show: false }, yAxis: { show: false },
24
+ series: [{
25
+ type: 'pie',
26
+ data: chart_data,
27
+ radius: donut ? ['40%', '75%'] : [0, '75%']
28
+ }],
29
+ tooltip: {
30
+ show: true,
31
+ valueFormatter: v => `${v?.toLocaleString()} ${unit || ''} `
32
+ }
33
+ };
34
+ return _jsx(ChartEcharts, { option: option });
35
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Graphique standard pour afficher des données annuelles
3
+ */
4
+ interface IYearSerieProps {
5
+ dataset: string;
6
+ title?: string;
7
+ yearControl?: string;
8
+ yearKey: string;
9
+ valueKey: string;
10
+ categoryKey?: string;
11
+ stack?: boolean;
12
+ yearMark?: number | string;
13
+ type?: 'bar' | 'line' | 'area';
14
+ }
15
+ export declare const ChartYearSerie: React.FC<IYearSerieProps>;
16
+ export {};
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Graphique standard pour afficher des données annuelles
4
+ */
5
+ import { from, op } from "arquero";
6
+ import { useDataset } from "../Dataset/hooks";
7
+ import { usePalette } from "../Palette/Palette";
8
+ import { ChartEcharts } from "./ChartEcharts";
9
+ export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, yearKey, yearMark, stack: stack_input, type: chart_type = 'bar', yearControl = 'year' }) => {
10
+ const stack = stack_input || chart_type == 'line' ? false : true; // Pas de stack par défaut pour le type line
11
+ const dataset = useDataset(dataset_id);
12
+ const data = dataset?.data;
13
+ const chart_data = categoryKey ? data && from(data).groupby(yearKey, categoryKey) //Somme par année et categorykey
14
+ .rollup({ [valueKey]: op.sum(valueKey) })
15
+ .groupby(yearKey).orderby(yearKey).objects()
16
+ :
17
+ data && from(data).groupby(yearKey) //Somme par année seulement
18
+ .rollup({ [valueKey]: op.sum(valueKey) }).orderby(yearKey)
19
+ .objects();
20
+ const distinct_cat = categoryKey ?
21
+ data && from(data).rollup({ cat: op.array_agg_distinct(categoryKey) }).object().cat
22
+ :
23
+ [valueKey];
24
+ const COLORS = usePalette({ nColors: distinct_cat?.length }) || [];
25
+ const series = distinct_cat ? distinct_cat.map((cat, idx) => ({
26
+ name: cat,
27
+ type: chart_type === 'area' ? 'line' : chart_type,
28
+ data: categoryKey ? chart_data?.filter((row) => row[categoryKey] === cat).map((row) => ([String(row[yearKey]), row[valueKey]]))
29
+ : chart_data?.map((row) => ([String(row[yearKey]), row[valueKey]])),
30
+ itemStyle: {
31
+ color: COLORS && COLORS[idx % COLORS.length],
32
+ },
33
+ stack: stack ? 'total' : undefined,
34
+ areaStyle: chart_type === 'area' ? {} : undefined,
35
+ markLine: idx === 0 && yearMark ? {
36
+ symbol: 'none',
37
+ data: [
38
+ { xAxis: String(yearMark) }
39
+ ]
40
+ } : undefined
41
+ })) : [];
42
+ function tooltipFormatter(params) {
43
+ if (!params || params.length === 0)
44
+ return '';
45
+ const year = params[0].value[0];
46
+ const lines = params.map((p) => {
47
+ const value = Number(p.value[1]).toLocaleString();
48
+ return `${p.marker} ${p.seriesName} <b><span style="display:inline-block; min-width:80px; text-align:right;">${value}</span></b>`;
49
+ });
50
+ return `<div>${year}</div>` + lines.join('<br/>');
51
+ }
52
+ const option = {
53
+ series: series,
54
+ legend: {
55
+ show: true,
56
+ bottom: 0
57
+ },
58
+ tooltip: {
59
+ trigger: 'axis',
60
+ formatter: tooltipFormatter
61
+ },
62
+ xAxis: {
63
+ type: 'time',
64
+ },
65
+ yAxis: {
66
+ type: 'value',
67
+ },
68
+ };
69
+ return (_jsx(ChartEcharts, { option: option }));
70
+ };
@@ -0,0 +1,21 @@
1
+ import React, { CSSProperties, ReactElement } from "react";
2
+ interface IControlProps {
3
+ children: ReactElement | ReactElement[];
4
+ style?: CSSProperties;
5
+ }
6
+ declare const Control: React.FC<IControlProps>;
7
+ export default Control;
8
+ export declare const useControl: (name: string) => string | undefined;
9
+ export declare const useAllControls: () => Record<string, any>;
10
+ export declare const list_to_options: (input?: string[] | number[] | {
11
+ label: string | number;
12
+ value: string | number;
13
+ }[]) => {
14
+ label: string | number;
15
+ value: string | number;
16
+ }[];
17
+ interface IControlProps {
18
+ children: ReactElement | ReactElement[];
19
+ }
20
+ export declare const DSL_Control: React.FC<IControlProps>;
21
+ export declare const ControlPreview: React.FC;
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Descriptions, Form, Layout } from "antd";
3
+ import React, { useContext, useEffect } from "react";
4
+ import { ControlContext } from "../DashboardPage/Page";
5
+ const { Header } = Layout;
6
+ /*
7
+ * Composant destiné à recevoir un Form avec les contrôles de la page
8
+ */
9
+ const Control = ({ children, style = {} }) => {
10
+ return (_jsx(Header, { style: {
11
+ padding: 12,
12
+ position: "sticky",
13
+ top: 0,
14
+ zIndex: 600, // maplibre top zIndex if 500
15
+ backgroundColor: "#fff",
16
+ height: "auto",
17
+ width: "100%",
18
+ ...style,
19
+ }, children: children }));
20
+ };
21
+ export default Control;
22
+ /*
23
+ * Hook pour accéder à un control spécifique de la page
24
+ */
25
+ export const useControl = (name) => {
26
+ const context_controls = useContext(ControlContext);
27
+ if (!context_controls) {
28
+ throw new Error("useControl must be used within a ControlProvider");
29
+ }
30
+ const { values } = context_controls;
31
+ const value = values[name];
32
+ return value;
33
+ };
34
+ /*
35
+ * Hook pour accéder à tous les controls utilisateur
36
+ */
37
+ export const useAllControls = () => {
38
+ const context_controls = useContext(ControlContext);
39
+ if (!context_controls) {
40
+ throw new Error("useControl must be used within a ControlProvider");
41
+ }
42
+ const { values } = context_controls;
43
+ return values;
44
+ };
45
+ /* Convenient function to return Options from list or Options */
46
+ export const list_to_options = (input = []) => {
47
+ if (input === undefined) {
48
+ return [];
49
+ }
50
+ return input.map((o) => {
51
+ if (typeof o == "string" || typeof o == "number") {
52
+ return { label: String(o), value: o };
53
+ }
54
+ return o;
55
+ });
56
+ };
57
+ export const DSL_Control = ({ children }) => {
58
+ const context_controls = useContext(ControlContext);
59
+ const [form] = Form.useForm();
60
+ useEffect(() => {
61
+ handleChange(form?.getFieldsValue(true)); // Appliquer les valeurs par défaut au contexte lors de l'initialisation du composant
62
+ }, []);
63
+ if (!context_controls) { //Le contexte peut être nul ?
64
+ throw new Error("useControl must be used within a ControlProvider");
65
+ }
66
+ const { values: _control, pushValue: pushControl } = context_controls;
67
+ const childrenArray = React.Children.toArray(children).filter((child) => React.isValidElement(child));
68
+ //Ajout des nouvelles valeurs de controles dans le contexte de la page
69
+ const handleChange = (changed_value) => {
70
+ pushControl(changed_value);
71
+ };
72
+ const initialValues = Object.fromEntries(childrenArray.filter((child) => child.props.options && child.props.options.length > 0).map((child) => [child.props.name, child.props.options[0].value])); // Initialisé avec la première valeur de chaque option
73
+ return (_jsx(Form, { onValuesChange: handleChange, layout: "inline", initialValues: initialValues, form: form, children: children }));
74
+ };
75
+ //TODO : ajouter la gestion des useSearchParameters (ici au dans la Page ?)
76
+ export const ControlPreview = ({}) => {
77
+ const controlValues = useAllControls();
78
+ const items = Object.entries(controlValues).map(([key, value]) => ({
79
+ key: key,
80
+ label: key,
81
+ children: _jsx("p", { children: value }),
82
+ }));
83
+ return (_jsx(Descriptions, { items: items }));
84
+ };
@@ -0,0 +1,19 @@
1
+ import type { RadioGroupProps } from 'antd';
2
+ import { SimpleRecord } from '../../types';
3
+ export declare const buildOptionsFromData: (data: SimpleRecord[], labelField?: string, valueField?: string) => {
4
+ label: string;
5
+ value: string | number;
6
+ }[];
7
+ type ExtendedRadioGroupProps = RadioGroupProps & {
8
+ name?: string;
9
+ dataset?: string;
10
+ options?: {
11
+ label: string;
12
+ value: string | number;
13
+ }[] | string[] | number[];
14
+ labelField?: string;
15
+ valueField?: string;
16
+ initalValue?: string | number;
17
+ };
18
+ export declare const Radio: React.FC<ExtendedRadioGroupProps>;
19
+ export {};
@@ -0,0 +1,27 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Radio as AntRadio, Form } from 'antd';
3
+ import { useDataset } from '../Dataset/hooks';
4
+ import { from } from 'arquero';
5
+ import { list_to_options } from './Control';
6
+ // On construit les options depuis le tableau de données, utiliser pour Radio et Select
7
+ export const buildOptionsFromData = (data, labelField = 'label', valueField = 'value') => {
8
+ const t = from(data);
9
+ return (t.select(labelField, valueField)
10
+ .dedupe(valueField)
11
+ .objects()
12
+ .map((row) => ({
13
+ label: row[labelField],
14
+ value: row[valueField],
15
+ })));
16
+ };
17
+ export const Radio = ({ dataset: datasetSource, options: input_options = [], labelField = 'label', valueField = 'value', name, initalValue: initalValue_input, ...rest }) => {
18
+ // Ici tu pourrais fetcher les données depuis un contexte/dataset si datasetSource est présent
19
+ const data = useDataset(datasetSource)?.data;
20
+ const options = list_to_options(input_options);
21
+ const data_options = options || data && buildOptionsFromData(data, labelField, valueField);
22
+ const initial_value = initalValue_input ? initalValue_input : data_options && data_options?.length > 0 && data_options[0].value;
23
+ if (data === undefined && options === undefined) {
24
+ return _jsx(_Fragment, {});
25
+ }
26
+ return (_jsx(Form.Item, { name: name, label: name, initialValue: initial_value, children: _jsx(AntRadio.Group, { options: data_options, value: initial_value, ...rest }) }));
27
+ };
@@ -0,0 +1,15 @@
1
+ import type { SelectProps } from 'antd';
2
+ type ExtendedSelectProps = Omit<SelectProps<any>, 'options'> & {
3
+ dataset?: string;
4
+ options?: {
5
+ label: string;
6
+ value: string | number;
7
+ }[] | string[] | number[];
8
+ initial_value?: string | number;
9
+ labelField?: string;
10
+ valueField?: string;
11
+ name?: string;
12
+ arrows?: boolean;
13
+ };
14
+ export declare const Select: React.FC<ExtendedSelectProps>;
15
+ export {};
@@ -0,0 +1,25 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useDataset } from '../Dataset/hooks';
3
+ import { buildOptionsFromData } from './Radio';
4
+ import NextPrevSelect from '../NextPrevSelect/NextPrevSelect';
5
+ import { list_to_options } from './Control';
6
+ // TODO : a fusionner avec NextPrevSelect pour n'avoir qu'un seul composant
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 }) => {
9
+ const options = list_to_options(input_options);
10
+ const data = useDataset(datasetSource)?.data;
11
+ const data_options = datasetSource ? (data && buildOptionsFromData(data, labelField, valueField)) : options;
12
+ const myOptions = data_options && data_options.map((o) => {
13
+ if (typeof o == "string" || typeof o == "number") {
14
+ return { label: o, value: o };
15
+ }
16
+ return o;
17
+ });
18
+ const initial_value = initial_value_in || myOptions && myOptions?.length > 0 && myOptions[0].value;
19
+ if (data_options === undefined) {
20
+ return _jsx(_Fragment, {});
21
+ }
22
+ return (_jsx(NextPrevSelect, { name: name, options: data_options, defaultValue: initial_value == null || initial_value == false
23
+ ? undefined
24
+ : initial_value, arrows: arrows, optionFilterProp: "label", ...rest }));
25
+ };
@@ -0,0 +1,11 @@
1
+ import { EChartsOption } from "echarts";
2
+ type EChartsSeriesTypes = ('line' | 'bar' | 'pie' | 'scatter');
3
+ interface IDashboardChartProps {
4
+ data?: any[];
5
+ chart_type?: EChartsSeriesTypes;
6
+ sql?: string;
7
+ echarts_option?: EChartsOption;
8
+ reverse_axies?: boolean;
9
+ }
10
+ declare const DashboardChart: React.FC<IDashboardChartProps>;
11
+ export default DashboardChart;