@geo2france/api-dashboard 1.16.0 → 1.18.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 (42) hide show
  1. package/README.MD +22 -19
  2. package/dist/components/Charts/ChartEcharts.d.ts +6 -2
  3. package/dist/components/Charts/ChartEcharts.js +1 -2
  4. package/dist/components/Charts/Pie.d.ts +21 -3
  5. package/dist/components/Charts/Pie.js +22 -3
  6. package/dist/components/Charts/Statistics.d.ts +9 -7
  7. package/dist/components/Charts/Statistics.js +12 -10
  8. package/dist/components/Charts/YearSerie.d.ts +1 -2
  9. package/dist/components/Charts/YearSerie.js +1 -0
  10. package/dist/components/Control/Control.d.ts +21 -5
  11. package/dist/components/Control/Control.js +28 -6
  12. package/dist/components/Control/Radio.d.ts +4 -4
  13. package/dist/components/Control/Radio.js +3 -0
  14. package/dist/components/Control/Select.d.ts +5 -3
  15. package/dist/components/Control/Select.js +4 -4
  16. package/dist/components/DashboardPage/Block.js +4 -2
  17. package/dist/components/DashboardPage/Intro.d.ts +12 -0
  18. package/dist/components/DashboardPage/Intro.js +21 -0
  19. package/dist/components/DashboardPage/Page.d.ts +0 -8
  20. package/dist/components/DashboardPage/Page.js +12 -6
  21. package/dist/components/Dataset/Dataset.js +2 -4
  22. package/dist/components/Dataset/Producer.js +2 -2
  23. package/dist/components/Dataset/Transform.d.ts +13 -3
  24. package/dist/components/Dataset/Transform.js +5 -4
  25. package/dist/components/Dataset/hooks.d.ts +5 -1
  26. package/dist/components/Dataset/hooks.js +26 -1
  27. package/dist/components/Debug/Debug.js +1 -1
  28. package/dist/components/Layout/DashboardApp.d.ts +4 -0
  29. package/dist/components/Layout/DashboardApp.js +5 -13
  30. package/dist/components/Layout/Footer.js +42 -4
  31. package/dist/components/Layout/Sider.d.ts +1 -0
  32. package/dist/components/Layout/Sider.js +20 -19
  33. package/dist/components/NextPrevSelect/NextPrevSelect.d.ts +7 -6
  34. package/dist/components/NextPrevSelect/NextPrevSelect.js +16 -12
  35. package/dist/data_providers/file/utils/axios.js +2 -15
  36. package/dist/dsl/index.d.ts +3 -2
  37. package/dist/dsl/index.js +3 -2
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +1 -0
  40. package/dist/utils/aggregator.d.ts +18 -0
  41. package/dist/utils/aggregator.js +49 -0
  42. package/package.json +25 -11
@@ -1,10 +1,9 @@
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 } 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";
@@ -58,8 +57,7 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
58
57
  };
59
58
  const datasetRegistryContext = useContext(DatasetRegistryContext);
60
59
  const allDatasets = useAllDatasets();
61
- const controlContext = useContext(ControlContext);
62
- const controls = controlContext?.values;
60
+ const controls = useAllControls();
63
61
  const providerContext = useContext(DataProviderContext);
64
62
  const provider = (providerUrl && getProviderFromType(providerType)(providerUrl)) || providerContext || provider_input;
65
63
  if (provider === undefined) {
@@ -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 datasets ? (_jsxs(_Fragment, { 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;
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<T = SimpleRecord[]> {
3
- children: string | ((data: T) => T);
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
- export declare const DSL_Transform: React.FC<ITransformProps>;
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
- * Les props sont utilisées dans le Dataset parent pour appliquer une (ou plusieurs fonction de transformation)
4
- */
5
- export const DSL_Transform = () => {
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
  };
@@ -2,7 +2,11 @@ import { dataset } from "./Dataset";
2
2
  export declare const useDataset: (dataset_id?: string) => dataset | undefined;
3
3
  export declare const useAllDatasets: () => dataset[];
4
4
  export declare const useDatasets: (dataset_ids?: string[]) => dataset[];
5
- export declare const useDatasetRegistry: () => {
5
+ /**
6
+ * Fonction permettant de créer le registre de dataset
7
+ * avec les méthodes nécessaires.
8
+ */
9
+ export declare const createDatasetRegistry: () => {
6
10
  register: (d: dataset) => void;
7
11
  clear: () => void;
8
12
  get: (dataset_id?: string) => dataset | undefined;
@@ -1,7 +1,28 @@
1
1
  import { useCallback, useContext, useState } from "react";
2
2
  import { DatasetRegistryContext } from "./context";
3
+ const demo_dataset = {
4
+ id: 'demo_dataset',
5
+ isError: false,
6
+ isFetching: false,
7
+ resource: 'demo_dataset',
8
+ data: [
9
+ { name: 'Mercure', type: 'Planète', satellites: 0, diameter_km: 4879 },
10
+ { name: 'Vénus', type: 'Planète', satellites: 0, diameter_km: 12104 },
11
+ { name: 'Terre', type: 'Planète', satellites: 1, diameter_km: 12742 },
12
+ { name: 'Mars', type: 'Planète', satellites: 2, diameter_km: 6779 },
13
+ { name: 'Jupiter', type: 'Planète', satellites: 79, diameter_km: 139820 },
14
+ { name: 'Saturne', type: 'Planète', satellites: 83, diameter_km: 116460 },
15
+ { name: 'Uranus', type: 'Planète', satellites: 27, diameter_km: 50724 },
16
+ { name: 'Neptune', type: 'Planète', satellites: 14, diameter_km: 49244 },
17
+ { name: 'Pluton', type: 'Planète naine', satellites: 5, diameter_km: 2370 },
18
+ { name: 'Cérès', type: 'Astéroïde', satellites: 0, diameter_km: 946 },
19
+ ]
20
+ };
3
21
  // 🔹 Hook pour récupérer un dataset unique
4
22
  export const useDataset = (dataset_id) => {
23
+ if (dataset_id == 'demo_dataset') {
24
+ return demo_dataset;
25
+ }
5
26
  const datasetRegistry = useContext(DatasetRegistryContext);
6
27
  if (dataset_id) {
7
28
  return datasetRegistry.get(dataset_id);
@@ -17,7 +38,11 @@ export const useDatasets = (dataset_ids) => {
17
38
  const datasets = useAllDatasets();
18
39
  return (datasets.filter(d => dataset_ids?.includes(d.id)));
19
40
  };
20
- export const useDatasetRegistry = () => {
41
+ /**
42
+ * Fonction permettant de créer le registre de dataset
43
+ * avec les méthodes nécessaires.
44
+ */
45
+ export const createDatasetRegistry = () => {
21
46
  /* DATASET */
22
47
  const [datasets, setdatasets] = useState({});
23
48
  const pushDataset = useCallback((d) => {
@@ -20,5 +20,5 @@ export const Debug = () => {
20
20
  label: _jsxs("span", { children: [_jsx(DatasetBadgeStatus, { isError: dataset?.isError, isFetching: dataset?.isFetching }), " ", dataset.id, " ", " ", _jsxs(Text, { type: "secondary", children: [" ", dataset?.resource, " "] }), " ", _jsx(Badge, { color: token.colorInfo, overflowCount: 9999, count: dataset?.data?.length })] }),
21
21
  children: _jsx(DataPreview, { dataset: dataset.id, pageSize: 3 })
22
22
  }));
23
- return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(BugOutlined, {}), type: "primary", onClick: () => setIsModalOpen(true), style: { top: 5 }, className: "debugFloatButton" }), _jsxs(Modal, { title: "Information concepteur", width: "90%", centered: true, styles: { content: { 'width': "100%", padding: 36 } }, closable: { 'aria-label': 'Custom Close Button' }, open: isModalOpen, onCancel: () => setIsModalOpen(false), footer: null, children: [_jsx(Title, { level: 5, children: "Jeux de donn\u00E9es " }), _jsx(Collapse, { accordion: true, items: items }), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Contr\u00F4les utilisateur " }), _jsx(ControlPreview, {}), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Palette " }), _jsx(PalettePreview, {})] })] }));
23
+ return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(BugOutlined, {}), type: "primary", onClick: () => setIsModalOpen(true), style: { bottom: 8, left: 8 }, className: "debugFloatButton" }), _jsxs(Modal, { title: "Information concepteur", width: "90%", centered: true, styles: { body: { 'width': "100%", padding: 36 } }, closable: { 'aria-label': 'Custom Close Button' }, open: isModalOpen, onCancel: () => setIsModalOpen(false), footer: null, children: [_jsx(Title, { level: 5, children: "Jeux de donn\u00E9es " }), _jsx(Collapse, { accordion: true, items: items }), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Contr\u00F4les utilisateur " }), _jsx(ControlPreview, {}), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Palette " }), _jsx(PalettePreview, {})] })] }));
24
24
  };
@@ -35,6 +35,10 @@ export interface DashboardConfig {
35
35
  * Active ou désactive le mode “slider” dans le pied de page (faire défiler les logos de partenaires).
36
36
  */
37
37
  footerSlider?: boolean;
38
+ /**
39
+ * Désactiver la mention à Géo2France
40
+ */
41
+ disablePoweredBy?: boolean;
38
42
  }
39
43
  declare const DashboardApp: React.FC<DashboardConfig>;
40
44
  export default DashboardApp;
@@ -7,11 +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, useState } from "react";
11
- import { ControlContext } from "../DashboardPage/Page";
10
+ import { createContext } from "react";
12
11
  import { HelmetProvider } from "react-helmet-async";
13
- import { useDatasetRegistry } from "../Dataset/hooks";
12
+ import { createDatasetRegistry } from "../Dataset/hooks";
14
13
  import { DatasetRegistryContext } from "../Dataset/context";
14
+ import { ControlContext, CreateControlesRegistry } from "../Control/Control";
15
15
  //import '../../index.css' //TODO a intégrer en jsx
16
16
  const queryClient = new QueryClient();
17
17
  const default_theme = {
@@ -33,16 +33,8 @@ const default_theme = {
33
33
  }
34
34
  };
35
35
  export const AppContext = createContext({});
36
- const DashboardApp = ({ routes, theme, logo, brands, footerSlider, title, subtitle }) => {
36
+ const DashboardApp = ({ routes, theme, logo, brands, footerSlider, title, subtitle, disablePoweredBy = false }) => {
37
37
  const context_values = { title, subtitle, logo };
38
- /* CONTROLS */
39
- const [controls, setControles] = useState({});
40
- const pushControl = (c) => {
41
- setControles(prev => ({
42
- ...prev,
43
- ...c
44
- }));
45
- };
46
- 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: useDatasetRegistry(), 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, poweredBy: !disablePoweredBy }), _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, {}) })] }) }) }) }) }) }) }) }) }));
47
39
  };
48
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: "hidden",
68
+ overflow: "visible",
44
69
  borderTop: "1px solid #ccc",
45
70
  zIndex: 600, // maplibre top zIndex if 500
46
- }, children: [isCollapsed && (_jsxs(Text, { type: "secondary", children: [app_context?.title, " - ", app_context?.subtitle] })), _jsx("div", { style: { display: isCollapsed ? "none" : "block", padding: "10px 0" }, children: slider
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
@@ -4,6 +4,7 @@ interface DbSiderProps {
4
4
  logo?: string;
5
5
  route_config?: RouteConfig[];
6
6
  style?: CSSProperties;
7
+ poweredBy?: boolean;
7
8
  }
8
9
  declare const DashboardSider: React.FC<DbSiderProps>;
9
10
  export default DashboardSider;
@@ -1,16 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useContext, useState } from "react";
3
- import { Layout, Menu, theme, Row, Col, Button, Divider } from "antd";
4
- import { NavLink, useLocation } from "react-router-dom";
3
+ import { Layout, Menu, theme, Row, Col, Button, Divider, Typography } from "antd";
4
+ import { Link, NavLink, useLocation } from "react-router-dom";
5
5
  import { generateMenuItems } from "../../utils/route_utils";
6
6
  import { AppContext } from "./DashboardApp";
7
7
  import { Icon } from "@iconify/react";
8
+ const { Text } = Typography;
8
9
  const style_img = {
9
10
  height: 52,
10
11
  maxWidth: "100%",
11
12
  objectFit: "contain"
12
13
  };
13
- const DashboardSider = ({ style, logo, route_config }) => {
14
+ const DashboardSider = ({ style, logo, route_config, poweredBy = true }) => {
14
15
  const { logo: appLogo, title } = useContext(AppContext);
15
16
  const { token } = theme.useToken();
16
17
  const { pathname: selectedKey } = useLocation();
@@ -32,21 +33,21 @@ const DashboardSider = ({ style, logo, route_config }) => {
32
33
  borderRight: "1px solid #ccc",
33
34
  ...style
34
35
  };
35
- return (_jsx(Layout.Sider, { theme: "light", collapsible: true, collapsedWidth: isMobile ? 40 : 80, collapsed: collapsed, onCollapse: toggleCollapsed, style: siderStyle, width: isMobile ? '80%' : 220, trigger: null, children: _jsxs(Row, { justify: "center", children: [_jsx(Col, { span: 24, children: _jsxs("div", { style: {
36
- margin: 4,
37
- display: "flex",
38
- justifyContent: "center",
39
- alignItems: "center",
40
- backgroundColor: token.colorBgElevated,
41
- }, children: [_jsx(NavLink, { to: "", style: {
42
- display: collapsed ? 'none' : undefined,
43
- marginTop: 8, marginLeft: 8
44
- }, children: _jsx("img", { style: style_img, src: appLogo, alt: title }) }), _jsx(Divider, { style: { display: collapsed ? 'none' : undefined }, type: "vertical" }), _jsx(Button, { type: "text", onClick: () => setCollapsed(!collapsed), icon: collapsed ? _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-right-rounded" }) : _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-left-rounded" }), style: {
45
- fontSize: '28px',
46
- width: 32,
47
- height: 32,
48
- //backgroundColor: token.colorFillSecondary,
49
- marginTop: 8
50
- } })] }) }), _jsx(Col, { span: 24, children: _jsx(Menu, { items: route_config && generateMenuItems(route_config), selectedKeys: [selectedKey], mode: "inline", style: { marginTop: "20px", width: "100%" } }) })] }) }));
36
+ return (_jsxs(Layout.Sider, { theme: "light", collapsible: true, collapsedWidth: isMobile ? 40 : 80, collapsed: collapsed, onCollapse: toggleCollapsed, style: siderStyle, width: isMobile ? '80%' : 220, trigger: null, children: [_jsxs(Row, { justify: "center", children: [_jsx(Col, { span: 24, children: _jsxs("div", { style: {
37
+ margin: 4,
38
+ display: "flex",
39
+ justifyContent: "center",
40
+ alignItems: "center",
41
+ backgroundColor: token.colorBgElevated,
42
+ }, children: [_jsx(NavLink, { to: "", style: {
43
+ display: collapsed ? 'none' : undefined,
44
+ marginTop: 8, marginLeft: 8
45
+ }, children: _jsx("img", { style: style_img, src: appLogo, alt: title }) }), _jsx(Divider, { style: { display: collapsed ? 'none' : undefined }, type: "vertical" }), _jsx(Button, { type: "text", onClick: () => setCollapsed(!collapsed), icon: collapsed ? _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-right-rounded" }) : _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-left-rounded" }), style: {
46
+ fontSize: '28px',
47
+ width: 32,
48
+ height: 32,
49
+ //backgroundColor: token.colorFillSecondary,
50
+ marginTop: 8
51
+ } })] }) }), _jsx(Col, { span: 24, children: _jsx(Menu, { items: route_config && generateMenuItems(route_config), selectedKeys: [selectedKey], mode: "inline", style: { marginTop: "20px", width: "100%" } }) })] }), (poweredBy && !collapsed) && _jsxs(Text, { type: "secondary", style: { position: "absolute", bottom: 0, left: 0, margin: 4 }, children: ["Propuls\u00E9 et fait avec \u2764\uFE0F par", " ", _jsx(Link, { to: "https://github.com/geo2france/api-dashboard", children: "G\u00E9o2France" })] })] }));
51
52
  };
52
53
  export default DashboardSider;
@@ -2,16 +2,17 @@ import React from 'react';
2
2
  import { CSSProperties } from "react";
3
3
  type NextPrevSelectProps = {
4
4
  options?: {
5
- label: string | number;
6
- value: string | number;
7
- }[] | string[] | number[];
5
+ label: string;
6
+ value: string;
7
+ }[] | string[];
8
8
  style?: CSSProperties;
9
- defaultValue?: string | number;
10
- value?: string | number;
11
- onChange?: (value: string | number) => void;
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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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, useState } from "react";
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] = useState(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
- value && handleChange(value);
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: name, 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, {}) })] }));
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;
@@ -1,20 +1,7 @@
1
1
  import axios from "axios";
2
2
  const axiosInstance = axios.create();
3
- axiosInstance.interceptors.response.use(
4
- // API WFS retourne toujours un code http 200, y compris en cas d'erreur.
5
- // Une réponse est présumée valide si elle renvoie un objet json valide. (Sinon c'est un xml)
6
- // TODO parser le XML retourné en cas d'erreur
7
- (response) => {
8
- if (typeof response.data === "object") {
9
- return response;
10
- }
11
- else {
12
- const customError = {
13
- message: response.data,
14
- statusCode: response.status,
15
- };
16
- return Promise.reject(customError);
17
- }
3
+ axiosInstance.interceptors.response.use((response) => {
4
+ return response;
18
5
  }, (error) => {
19
6
  const customError = {
20
7
  ...error,
@@ -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 { DSL_Transform as Transform } from "../components/Dataset/Transform";
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";
@@ -21,4 +21,5 @@ import { Statistics, StatisticsCollection } from "../components/Charts/Statistic
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 };
24
+ import { Intro } from "../components/DashboardPage/Intro";
25
+ export { Dashboard, Dataset, Provider, Transform, Join, Filter, Section, Intro, 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 { DSL_Transform as Transform } from "../components/Dataset/Transform";
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";
@@ -21,4 +21,5 @@ import { Statistics, StatisticsCollection } from "../components/Charts/Statistic
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 };
24
+ import { Intro } from "../components/DashboardPage/Intro";
25
+ export { Dashboard, Dataset, Provider, Transform, Join, Filter, Section, Intro, 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/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { useMapControl } from "./utils/useMapControl";
7
7
  export { BaseRecordToGeojsonPoint } from "./utils/baserecordtogeojsonpoint";
8
8
  export { cardStyles } from "./utils/cardStyles";
9
9
  export { merge_others } from "./utils/merge_others";
10
+ export { aggregator } from "./utils/aggregator";
10
11
  import KeyFigure from "./components/KeyFigure/KeyFigure";
11
12
  import LoadingContainer from "./components/LoadingContainer/LoadingContainer";
12
13
  import FlipCard from "./components/FlipCard/FlipCard";
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export { useMapControl } from "./utils/useMapControl";
9
9
  export { BaseRecordToGeojsonPoint } from "./utils/baserecordtogeojsonpoint";
10
10
  export { cardStyles } from "./utils/cardStyles";
11
11
  export { merge_others } from "./utils/merge_others";
12
+ export { aggregator } from "./utils/aggregator";
12
13
  // Components
13
14
  import KeyFigure from "./components/KeyFigure/KeyFigure";
14
15
  import LoadingContainer from "./components/LoadingContainer/LoadingContainer";
@@ -0,0 +1,18 @@
1
+ import { SimpleRecord } from "../types";
2
+ interface AggregatorParams {
3
+ /** Tableau de données */
4
+ data?: SimpleRecord[];
5
+ /** Colonne à aggréger */
6
+ dataKey?: string;
7
+ /** Agregat */
8
+ aggregate: "last" | "first" | "sum" | "lastNotNull" | "min" | "max" | "count" | "mean" | "countDistinct" | "countMissing";
9
+ }
10
+ interface AggregatorResult {
11
+ /** Ligne retenue (pour "last", "first", "lastNotNull") */
12
+ row?: SimpleRecord;
13
+ /** Valeur agrégée */
14
+ value?: number;
15
+ }
16
+ /** Fonction permettant d'agréger une colonne d'un dataset */
17
+ export declare const aggregator: ({ data, dataKey, aggregate }: AggregatorParams) => AggregatorResult;
18
+ export {};
@@ -0,0 +1,49 @@
1
+ import { from, op } from "arquero";
2
+ /** Fonction permettant d'agréger une colonne d'un dataset */
3
+ export const aggregator = ({ data, dataKey, aggregate }) => {
4
+ if (data == undefined || dataKey == undefined || data.length < 1) {
5
+ return { row: undefined, value: undefined };
6
+ }
7
+ switch (aggregate) {
8
+ case "last": {
9
+ const row = data.slice(-1)[0];
10
+ return { row, value: Number(row[dataKey]) };
11
+ }
12
+ case "first": {
13
+ const row = data[0];
14
+ return { row, value: Number(row[dataKey]) };
15
+ }
16
+ case "lastNotNull": {
17
+ const row = data.filter(r => r[dataKey] != null).slice(-1)?.[0];
18
+ return { row, value: Number(row?.[dataKey]) };
19
+ }
20
+ case "sum": {
21
+ const value = from(data).rollup({ value: op.sum(dataKey) }).object().value;
22
+ return { row: undefined, value };
23
+ }
24
+ case "min": {
25
+ const value = from(data).rollup({ value: op.min(dataKey) }).object().value;
26
+ return { row: undefined, value };
27
+ }
28
+ case "max": {
29
+ const value = from(data).rollup({ value: op.max(dataKey) }).object().value;
30
+ return { row: undefined, value };
31
+ }
32
+ case "count": {
33
+ const value = from(data).rollup({ value: op.valid(dataKey) }).object().value;
34
+ return { row: undefined, value };
35
+ }
36
+ case "mean": {
37
+ const value = from(data).rollup({ value: op.average(dataKey) }).object().value;
38
+ return { row: undefined, value };
39
+ }
40
+ case "countDistinct": {
41
+ const value = from(data).rollup({ value: op.distinct(dataKey) }).object().value;
42
+ return { row: undefined, value };
43
+ }
44
+ case "countMissing": {
45
+ const value = from(data).rollup({ value: op.invalid(dataKey) }).object().value;
46
+ return { row: undefined, value };
47
+ }
48
+ }
49
+ };