@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.
- package/README.MD +22 -19
- package/dist/components/Charts/ChartEcharts.d.ts +6 -2
- package/dist/components/Charts/ChartEcharts.js +1 -2
- package/dist/components/Charts/Pie.d.ts +21 -3
- package/dist/components/Charts/Pie.js +22 -3
- package/dist/components/Charts/Statistics.d.ts +9 -7
- package/dist/components/Charts/Statistics.js +12 -10
- package/dist/components/Charts/YearSerie.d.ts +1 -2
- package/dist/components/Charts/YearSerie.js +1 -0
- 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 +4 -2
- package/dist/components/DashboardPage/Intro.d.ts +12 -0
- package/dist/components/DashboardPage/Intro.js +21 -0
- package/dist/components/DashboardPage/Page.d.ts +0 -8
- package/dist/components/DashboardPage/Page.js +12 -6
- package/dist/components/Dataset/Dataset.js +2 -4
- 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/hooks.d.ts +5 -1
- package/dist/components/Dataset/hooks.js +26 -1
- package/dist/components/Debug/Debug.js +1 -1
- package/dist/components/Layout/DashboardApp.d.ts +4 -0
- package/dist/components/Layout/DashboardApp.js +5 -13
- package/dist/components/Layout/Footer.js +42 -4
- package/dist/components/Layout/Sider.d.ts +1 -0
- package/dist/components/Layout/Sider.js +20 -19
- package/dist/components/NextPrevSelect/NextPrevSelect.d.ts +7 -6
- package/dist/components/NextPrevSelect/NextPrevSelect.js +16 -12
- package/dist/data_providers/file/utils/axios.js +2 -15
- package/dist/dsl/index.d.ts +3 -2
- package/dist/dsl/index.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/aggregator.d.ts +18 -0
- package/dist/utils/aggregator.js +49 -0
- 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
|
|
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
|
|
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
|
};
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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: {
|
|
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
|
|
11
|
-
import { ControlContext } from "../DashboardPage/Page";
|
|
10
|
+
import { createContext } from "react";
|
|
12
11
|
import { HelmetProvider } from "react-helmet-async";
|
|
13
|
-
import {
|
|
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
|
-
/*
|
|
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: "
|
|
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
|
|
@@ -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 (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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;
|
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
const axiosInstance = axios.create();
|
|
3
|
-
axiosInstance.interceptors.response.use(
|
|
4
|
-
|
|
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,
|
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";
|
|
@@ -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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
+
};
|