diff --git a/.eslintrc.json b/.eslintrc.json
index ff57dffacbb..d5483ab0790 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -47,6 +47,8 @@
"tabWidth": 2,
"ignoreComments": true
}],
+ "no-unexpected-multiline": "off",
+ "no-unused-vars": "warn",
"no-useless-escape": "off",
"no-dupe-keys": "off",
"react/no-direct-mutation-state": "off",
diff --git a/modules/dashboard/php/widget.class.inc b/modules/dashboard/php/widget.class.inc
index dad176c7401..f74c474e8ca 100644
--- a/modules/dashboard/php/widget.class.inc
+++ b/modules/dashboard/php/widget.class.inc
@@ -33,6 +33,8 @@ class Widget implements \LORIS\GUI\Widget
private WidgetContent $_content;
private WidgetDisplayProps $_displayprops;
private ?WidgetDependencies $_dependencies;
+ private $_template = 'panel.tpl';
+ private $_template_variables = [];
/**
* Construct a dashboard widget with the specified properties.
@@ -54,6 +56,30 @@ class Widget implements \LORIS\GUI\Widget
$this->_dependencies = $deps;
}
+ /**
+ * The setTemplate - change default smarty template
+ *
+ * @param string $template_file The template file to use.
+ *
+ * @return void
+ */
+ public function setTemplate(string $template_file)
+ {
+ $this->_template = $template_file;
+ }
+
+ /**
+ * The setTemplateVariables - set additional smarty template variables
+ *
+ * @param array $template_variables The template variables to use.
+ *
+ * @return void
+ */
+ public function setTemplateVariables(array $template_variables)
+ {
+ $this->_template_variables = $template_variables;
+ }
+
/**
* Return the size of this widget.
*
@@ -119,8 +145,9 @@ class Widget implements \LORIS\GUI\Widget
'body' => $this->_content->getBody(),
'footer' => $this->_content->getFooter(),
'menus' => $this->_displayprops->getMenus(),
+ ...$this->_template_variables,
]
);
- return $renderer->fetch("panel.tpl");
+ return $renderer->fetch($this->_template);
}
}
diff --git a/modules/dashboard/templates/div.tpl b/modules/dashboard/templates/div.tpl
new file mode 100644
index 00000000000..5d63ecd7e84
--- /dev/null
+++ b/modules/dashboard/templates/div.tpl
@@ -0,0 +1 @@
+
{$body}
diff --git a/modules/dashboard/test/DashboardTest.php b/modules/dashboard/test/DashboardTest.php
index 835d80979b2..addaa3c310d 100644
--- a/modules/dashboard/test/DashboardTest.php
+++ b/modules/dashboard/test/DashboardTest.php
@@ -356,27 +356,31 @@ public function testDashboardRecruitmentView()
{
$this->safeGet($this->url . '/dashboard/');
$views = $this->safeFindElement(
- WebDriverBy::Xpath(
- "//*[@id='lorisworkspace']/div[1]".
- "/div[2]/div[1]/div/button"
+ WebDriverBy::cssSelector(
+ "#statistics_widgets .panel:nth-child(1) .views button"
)
);
$views->click();
$assertText1 = $this->safeFindElement(
- WebDriverBy::XPath(
- "//*[@id='lorisworkspace']/div[1]".
- "/div[2]/div[1]/div/ul/li[1]/a"
+ WebDriverBy::cssSelector(
+ "#statistics_widgets .panel:nth-child(1)".
+ " .dropdown-menu li:nth-child(1)"
)
)->getText();
+
$assertText2 = $this->safeFindElement(
- WebDriverBy::XPath(
- "//*[@id='lorisworkspace']/div[1]".
- "/div[2]/div[1]/div/ul/li[2]/a"
+ WebDriverBy::cssSelector(
+ "#statistics_widgets .panel:nth-child(1)".
+ " .dropdown-menu li:nth-child(2)"
)
)->getText();
- $this->assertStringContainsString("View overall recruitment", $assertText1);
- $this->assertStringContainsString("View site breakdown", $assertText2);
+
+ $this->assertStringContainsString("Recruitment - overall", $assertText1);
+ $this->assertStringContainsString(
+ "Recruitment - site breakdown",
+ $assertText2
+ );
}
/**
@@ -667,8 +671,11 @@ private function _testPlan7And8()
{
$this->safeGet($this->url . '/dashboard/');
$testText = $this->safeFindElement(
- WebDriverBy::cssSelector("#scan-line-chart-panel")
+ WebDriverBy::cssSelector(
+ "#statistics_studyprogression .panel-body div:nth-child(1)"
+ )
)->getText();
+
$this->assertStringContainsString(
"Scan sessions per site",
$testText
diff --git a/modules/statistics/.gitignore b/modules/statistics/.gitignore
new file mode 100644
index 00000000000..dd2b3c56afd
--- /dev/null
+++ b/modules/statistics/.gitignore
@@ -0,0 +1 @@
+js/WidgetIndex.js
diff --git a/modules/statistics/js/recruitment.js b/modules/statistics/js/recruitment.js
deleted file mode 100644
index e72af1c26ca..00000000000
--- a/modules/statistics/js/recruitment.js
+++ /dev/null
@@ -1,110 +0,0 @@
-$(document).ready(function() {
- // Colours for all charts broken down by only by site
- var siteColours = [
- '#F0CC00', '#27328C', '#2DC3D0', '#4AE8C2', '#D90074', '#7900DB', '#FF8000',
- '#0FB500', '#CC0000', '#DB9CFF', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2',
- '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'
- ];
- // Colours for the recruitment bar chart: breakdown by sex
- var sexColours = ['#2FA4E7', '#1C70B6'];
- $('.progress-bar').tooltip();
-
- // Open the appropriate charts from the "views" dropdown menus
- $(".dropdown-menu a").click(function () {
- $(this).parent().siblings().removeClass("active");
- $(this).parent().addClass("active");
- $($(this).parent().siblings().children("a")).each(function () {
- $(document.getElementById(this.getAttribute('data-target'))).addClass("hidden");
- });
- $(document.getElementById(this.getAttribute('data-target'))).removeClass("hidden");
-
- /* Make sure the chart variables are defined before resizing
- * They may not be defined on initial page load because they are created through
- * an AJAX request.
- */
- if (typeof recruitmentPieChart !== 'undefined') {
- recruitmentPieChart.resize();
- }
- if (typeof recruitmentBarChart !== 'undefined') {
- recruitmentBarChart.resize();
- }
- });
-
- function formatPieData(data) {
- "use strict";
- var processedData = new Array();
- for (var i in data) {
- var siteData = [data[i].label, data[i].total];
- processedData.push(siteData);
- }
- return processedData;
- }
-
- function formatBarData(data) {
- "use strict";
- var processedData = new Array();
- if (data.datasets) {
- var females = ['Female'];
- processedData.push(females.concat(data.datasets.female));
- var males = ['Male'];
- processedData.push(males.concat(data.datasets.male));
- }
- return processedData;
- }
- // AJAX to get pie chart data
- $.ajax({
- url: loris.BaseURL + '/statistics/charts/siterecruitment_pie',
- type: 'get',
- success: function(jsonData) {
- var recruitmentPieData = formatPieData(jsonData);
- recruitmentPieChart = c3.generate({
- bindto: '#recruitmentPieChart',
- data: {
- columns: recruitmentPieData,
- type : 'pie'
- },
- color: {
- pattern: siteColours
- }
- });
- },
- error: function(xhr, desc, err) {
- console.log(xhr);
- console.log("Details: " + desc + "\nError:" + err);
- }
- });
-
- // AJAX to get bar chart data
- $.ajax({
- url: loris.BaseURL + '/statistics/charts/siterecruitment_bysex',
- type: 'get',
- success: function(data) {
- var recruitmentBarData = formatBarData(data);
- var recruitmentBarLabels = data.labels;
- recruitmentBarChart = c3.generate({
- bindto: '#recruitmentBarChart',
- data: {
- columns: recruitmentBarData,
- type: 'bar'
- },
- axis: {
- x: {
- type : 'categorized',
- categories: recruitmentBarLabels
- },
- y: {
- label: 'Candidates registered'
- }
- },
- color: {
- pattern: sexColours
- }
- });
- },
- error: function(xhr, desc, err) {
- console.log(xhr);
- console.log("Details: " + desc + "\nError:" + err);
- }
- });
-}
-);
diff --git a/modules/statistics/js/studyprogression.js b/modules/statistics/js/studyprogression.js
deleted file mode 100644
index c2a6c42e18e..00000000000
--- a/modules/statistics/js/studyprogression.js
+++ /dev/null
@@ -1,212 +0,0 @@
-$(document).ready(function() {
-
- // Colours for all charts broken down by only by site
- var siteColours = [
- '#F0CC00', '#27328C', '#2DC3D0', '#4AE8C2', '#D90074', '#7900DB', '#FF8000',
- '#0FB500', '#CC0000', '#DB9CFF', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2',
- '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'
- ];
-
- // Open the appropriate charts from the "views" dropdown menus
- $(".dropdown-menu a").click(function () {
- $(this).parent().siblings().removeClass("active");
- $(this).parent().addClass("active");
- $($(this).parent().siblings().children("a")).each(function () {
- $(document.getElementById(this.getAttribute('data-target'))).addClass("hidden");
- });
- $(document.getElementById(this.getAttribute('data-target'))).removeClass("hidden");
-
- if (typeof recruitmentLineChart !== 'undefined') {
- recruitmentLineChart.resize();
- }
- if (typeof scanLineChart !== 'undefined') {
- scanLineChart.resize();
- }
- });
-
- function formatLineData(data) {
- "use strict";
- var processedData = new Array();
- var labels = new Array();
- labels.push('x');
- for (var i in data.labels) {
- labels.push(data.labels[i]);
- }
- processedData.push(labels);
- for (var i in data.datasets) {
- var dataset = new Array();
- dataset.push(data.datasets[i].name);
- processedData.push(dataset.concat(data.datasets[i].data));
- }
- var totals = new Array();
- totals.push("Total");
- for(var j=0; j' + id;
- })
- .each(function(id) {
- d3.select(this).select('span').style('background-color', scanLineChart.color(id));
- })
- .on('mouseover', function(id) {
- scanLineChart.focus(id);
- })
- .on('mouseout', function(id) {
- scanLineChart.revert();
- })
- .on('click', function(id) {
- $(this).toggleClass("c3-legend-item-hidden")
- scanLineChart.toggle(id);
- });
- },
- error: function(xhr, desc, err) {
- console.log(xhr);
- console.log("Details: " + desc + "\nError:" + err);
- }
- });
- // AJAX to get recruitment line chart data
- $.ajax({
- url: loris.BaseURL + '/statistics/charts/siterecruitment_line',
- type: 'get',
- success: function(data) {
- let legendNames = [];
- for (let j=0; j' + id;
- })
- .each(function(id) {
- d3.select(this).select('span').style('background-color', recruitmentLineChart.color(id));
- })
- .on('mouseover', function(id) {
- recruitmentLineChart.focus(id);
- })
- .on('mouseout', function(id) {
- recruitmentLineChart.revert();
- })
- .on('click', function(id) {
- $(this).toggleClass("c3-legend-item-hidden")
- recruitmentLineChart.toggle(id);
- });
- },
- error: function(xhr, desc, err) {
- console.log(xhr);
- console.log("Details: " + desc + "\nError:" + err);
- }
- });
-
-});
diff --git a/modules/statistics/jsx/Fetch.js b/modules/statistics/jsx/Fetch.js
new file mode 100644
index 00000000000..b9c7c349773
--- /dev/null
+++ b/modules/statistics/jsx/Fetch.js
@@ -0,0 +1,41 @@
+// todo move to jslib
+// after params have been adjusted
+// for get or post requests.
+
+/**
+ * fetchData - retrieve data from the provided URL.
+ *
+ * @param {string} url for the request
+ * @return {object} json retrieved from the server.
+ */
+const fetchData = async (url) => {
+ let response;
+ try {
+ response = await fetch(url,
+ {
+ method: 'GET',
+ credentials: 'same-origin',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+ if (response.ok) {
+ // Success
+ return await response.json();
+ }
+ } catch (e) {
+ let errorMessage;
+ if (response.status) {
+ // Error from the response.
+ errorMessage = `An error occurred: ${response.status}`;
+ } else {
+ // All other possible errors.
+ errorMessage = `An error occurred: ${e.message}`;
+ }
+ throw new Error(errorMessage);
+ }
+};
+export {
+ fetchData,
+};
diff --git a/modules/statistics/jsx/WidgetIndex.js b/modules/statistics/jsx/WidgetIndex.js
new file mode 100644
index 00000000000..7e1dbcbf6f5
--- /dev/null
+++ b/modules/statistics/jsx/WidgetIndex.js
@@ -0,0 +1,77 @@
+import {createRoot} from 'react-dom/client';
+import React, {useEffect, useState} from 'react';
+import PropTypes from 'prop-types';
+import Recruitment from './widgets/recruitment';
+import StudyProgression from './widgets/studyprogression';
+import {fetchData} from './Fetch';
+import {
+ recruitmentCharts,
+ studyProgressionCharts,
+} from './widgets/chartBuilder';
+
+/**
+ * WidgetIndex - the main window.
+ *
+ * @param {object} props
+ * @return {JSX.Element}
+ */
+const WidgetIndex = (props) => {
+ const [recruitmentData, setRecruitmentData] = useState({});
+ const [studyProgressionData, setStudyProgressionData] = useState({});
+ /**
+ * Similar to componentDidMount and componentDidUpdate.
+ */
+ useEffect(() => {
+ /**
+ * setup - fetch recruitment and study progression data.
+ *
+ * @return {Promise}
+ */
+ const setup = async () => {
+ const data = await fetchData(
+ `${props.baseURL}/Widgets`
+ );
+ setRecruitmentData(data);
+ setStudyProgressionData(data);
+ // setup statistics for c3.js charts.
+ await studyProgressionCharts();
+ await recruitmentCharts();
+ };
+ setup().catch((error) => {
+ console.error(error);
+ });
+ }, []);
+
+ /**
+ * Renders the React component.
+ *
+ * @return {JSX.Element} - React markup for component.
+ */
+ return (
+ <>
+
+
+ >
+ );
+};
+WidgetIndex.propTypes = {
+ baseURL: PropTypes.string,
+};
+
+/**
+ * Render StatisticsIndex on page load.
+ */
+window.addEventListener('load', () => {
+ const root = createRoot(
+ document.getElementById('statistics_widgets')
+ );
+ root.render(
+
+ );
+});
diff --git a/modules/statistics/jsx/widgets/chartBuilder.js b/modules/statistics/jsx/widgets/chartBuilder.js
new file mode 100644
index 00000000000..eb48d62dbd8
--- /dev/null
+++ b/modules/statistics/jsx/widgets/chartBuilder.js
@@ -0,0 +1,343 @@
+import 'c3/c3.min.css';
+import c3 from 'c3';
+import {select} from 'd3';
+import {fetchData} from '../Fetch';
+
+const baseURL = window.location.origin;
+
+// Charts
+let scanLineChart;
+let recruitmentPieChart;
+let recruitmentBarChart;
+let recruitmentLineChart;
+
+// Colours for all charts broken down by only by site
+const siteColours = [
+ '#F0CC00', '#27328C', '#2DC3D0', '#4AE8C2', '#D90074', '#7900DB', '#FF8000',
+ '#0FB500', '#CC0000', '#DB9CFF', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2',
+ '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5',
+];
+
+// Colours for the recruitment bar chart: breakdown by sex
+const sexColours = ['#2FA4E7', '#1C70B6'];
+
+/**
+ * elementVisibility - used to resize charts when element becomes visible.
+ *
+ * @param {HTMLElement} element
+ * @param {function} callback
+ */
+const elementVisibility = (element, callback) => {
+ const options = {
+ root: document.documentElement,
+ };
+ const observer = new IntersectionObserver((entries, observer) => {
+ entries.forEach((entry) => {
+ callback(entry.intersectionRatio > 0);
+ });
+ }, options);
+ observer.observe(element);
+};
+
+/**
+ * formatPieData - used for the recruitment widget
+ *
+ * @param {object} data
+ * @return {*[]}
+ */
+const formatPieData = (data) => {
+ const processedData = [];
+ for (const [i] of Object.entries(data)) {
+ const siteData = [data[i].label, data[i].total];
+ processedData.push(siteData);
+ }
+ return processedData;
+};
+
+/**
+ * formatBarData - used for the recruitment widget
+ *
+ * @param {object} data
+ * @return {*[]}
+ */
+const formatBarData = (data) => {
+ const processedData = [];
+ if (data['datasets']) {
+ const females = ['Female'];
+ processedData.push(females.concat(data['datasets']['female']));
+ }
+ if (data['datasets']) {
+ const males = ['Male'];
+ processedData.push(males.concat(data['datasets']['male']));
+ }
+ return processedData;
+};
+
+/**
+ * formatLineData - used for the study progression widget
+ *
+ * @param {object} data
+ * @return {*[]}
+ */
+const formatLineData = (data) => {
+ const processedData = [];
+ const labels = [];
+ labels.push('x');
+ for (const [i] of Object.entries(data.labels)) {
+ labels.push(data.labels[i]);
+ }
+ processedData.push(labels);
+ for (const [i] of Object.entries(data['datasets'])) {
+ const dataset = [];
+ dataset.push(data['datasets'][i].name);
+ processedData.push(dataset.concat(data['datasets'][i].data));
+ }
+ const totals = [];
+ totals.push('Total');
+ for (let j = 0; j < data['datasets'][0].data.length; j++) {
+ let total = 0;
+ for (let i = 0; i < data['datasets'].length; i++) {
+ total += parseInt(data['datasets'][i].data[j]);
+ }
+ totals.push(total);
+ }
+ processedData.push(totals);
+ return processedData;
+};
+
+/**
+ * maxY - used for the study progression widget
+ *
+ * @param {object} data
+ * @return {number}
+ */
+const maxY = (data) => {
+ let maxi = 0;
+ for (let j = 0; j < data['datasets'][0].data.length; j++) {
+ for (let i = 0; i < data['datasets'].length; i++) {
+ maxi = Math.max(maxi, parseInt(data['datasets'][i].data[j]));
+ }
+ }
+ return maxi;
+};
+
+/**
+ * recruitmentCharts - fetch data for recruitments
+ */
+const recruitmentCharts = async () => {
+ // fetch data for the pie chart.
+ let data = await fetchData(
+ `${baseURL}/statistics/charts/siterecruitment_pie`,
+ );
+ const recruitmentPieData = formatPieData(data);
+ recruitmentPieChart = c3.generate({
+ bindto: '#recruitmentPieChart',
+ size: {
+ width: 227,
+ },
+ data: {
+ columns: recruitmentPieData,
+ type: 'pie',
+ },
+ color: {
+ pattern: siteColours,
+ },
+ });
+ elementVisibility(recruitmentPieChart.element, (visible) => {
+ if (visible) {
+ recruitmentPieChart.resize();
+ }
+ });
+
+ // fetch data for the bar chart.
+ data = await fetchData(
+ `${baseURL}/statistics/charts/siterecruitment_bysex`,
+ );
+ const recruitmentBarData = formatBarData(data);
+ const recruitmentBarLabels = data.labels;
+ recruitmentBarChart = c3.generate({
+ bindto: '#recruitmentBarChart',
+ size: {
+ width: 461,
+ },
+ data: {
+ columns: recruitmentBarData,
+ type: 'bar',
+ },
+ axis: {
+ x: {
+ type: 'categorized',
+ categories: recruitmentBarLabels,
+ },
+ y: {
+ label: 'Candidates registered',
+ },
+ },
+ color: {
+ pattern: sexColours,
+ },
+ });
+ elementVisibility(recruitmentBarChart.element, (visible) => {
+ if (visible) {
+ recruitmentBarChart.resize();
+ }
+ });
+};
+
+/**
+ * studyProgressionCharts - fetch data for study progression
+ */
+const studyProgressionCharts = async () => {
+ // fetch data for the line chart.
+ let data = await fetchData(
+ `${baseURL}/statistics/charts/scans_bymonth`,
+ );
+ let legendNames = [];
+ for (let j = 0; j < data['datasets'].length; j++) {
+ legendNames.push(data['datasets'][j].name);
+ }
+ const scanLineData = formatLineData(data);
+ scanLineChart = c3.generate({
+ size: {
+ height: '100%',
+ },
+ bindto: '#scanChart',
+ data: {
+ x: 'x',
+ xFormat: '%m-%Y',
+ columns: scanLineData,
+ type: 'area-spline',
+ },
+ legend: {
+ show: false,
+ },
+ axis: {
+ x: {
+ type: 'timeseries',
+ tick: {
+ format: '%m-%Y',
+ },
+ },
+ y: {
+ max: maxY(data),
+ label: 'Scans',
+ },
+ },
+ zoom: {
+ enabled: true,
+ },
+ color: {
+ pattern: siteColours,
+ },
+ });
+ select('.scanChartLegend')
+ .insert('div', '.scanChart')
+ .attr('class', 'legend')
+ .selectAll('div').data(legendNames).enter()
+ .append('div')
+ .attr('data-id', function(id) {
+ return id;
+ })
+ .html(function(id) {
+ return '' + id;
+ })
+ .each(function(id) {
+ select(this).select('span').style(
+ 'background-color', scanLineChart.color(id),
+ );
+ })
+ .on('mouseover', function(id) {
+ scanLineChart.focus(id);
+ })
+ .on('mouseout', function(id) {
+ scanLineChart.revert();
+ })
+ .on('click', function(id) {
+ scanLineChart.toggle(id);
+ });
+ elementVisibility(scanLineChart.element, (visible) => {
+ if (visible) {
+ scanLineChart.resize();
+ }
+ });
+ // scanLineChart.resize();
+
+ // fetch data for the line chart.
+ data = await fetchData(
+ `${baseURL}/statistics/charts/siterecruitment_line`,
+ );
+ legendNames = [];
+ for (let j = 0; j < data['datasets'].length; j++) {
+ legendNames.push(data['datasets'][j].name);
+ }
+ const recruitmentLineData = formatLineData(data);
+ recruitmentLineChart = c3.generate({
+ size: {
+ height: '100%',
+ },
+ bindto: '#recruitmentChart',
+ data: {
+ x: 'x',
+ xFormat: '%m-%Y',
+ columns: recruitmentLineData,
+ type: 'area-spline',
+ },
+ legend: {
+ show: false,
+ },
+ axis: {
+ x: {
+ type: 'timeseries',
+ tick: {
+ format: '%m-%Y',
+ },
+ },
+ y: {
+ max: maxY(data),
+ label: 'Candidates registered',
+ },
+ },
+ zoom: {
+ enabled: true,
+ },
+ color: {
+ pattern: siteColours,
+ },
+ });
+ select('.recruitmentChartLegend')
+ .insert('div', '.recruitmentChart')
+ .attr('class', 'legend')
+ .selectAll('div').data(legendNames).enter()
+ .append('div')
+ .attr('data-id', function(id) {
+ return id;
+ })
+ .html(function(id) {
+ return '' + id;
+ })
+ .each(function(id) {
+ select(this).select('span').style(
+ 'background-color',
+ recruitmentLineChart.color(id));
+ })
+ .on('mouseover', function(id) {
+ recruitmentLineChart.focus(id);
+ })
+ .on('mouseout', function(id) {
+ recruitmentLineChart.revert();
+ })
+ .on('click', function(id) {
+ recruitmentLineChart.toggle(id);
+ });
+ elementVisibility(recruitmentLineChart.element, (visible) => {
+ if (visible) {
+ recruitmentLineChart.resize();
+ }
+ });
+ // recruitmentLineChart.resize();
+};
+
+export {
+ recruitmentCharts,
+ studyProgressionCharts,
+};
diff --git a/modules/statistics/jsx/widgets/recruitment.js b/modules/statistics/jsx/widgets/recruitment.js
new file mode 100644
index 00000000000..6f4225edf6c
--- /dev/null
+++ b/modules/statistics/jsx/widgets/recruitment.js
@@ -0,0 +1,205 @@
+import React, {useEffect, useState} from 'react';
+import PropTypes from 'prop-types';
+import Loader from 'Loader';
+import Panel from 'jsx/Panel';
+
+/**
+ * Recruitment - a widget containing statistics for recruitment data.
+ *
+ * @param {object} props
+ * @return {JSX.Element}
+ */
+const Recruitment = (props) => {
+ const [loading, setLoading] = useState(true);
+ const [overall, setOverall] = useState({});
+ const [siteBreakdown, setSiteBreakdown] = useState({});
+ const [projectBreakdown, setProjectBreakdown] = useState({});
+
+ /**
+ * useEffect - modified to run when props.data updates.
+ */
+ useEffect(() => {
+ const json = props.data;
+ if (json && Object.keys(json).length !== 0) {
+ const overallData = (
+
+ {progressBarBuilder(json['recruitment']['overall'])}
+
+ );
+ let siteBreakdownData;
+ if (json['recruitment']['overall'] &&
+ json['recruitment']['overall']['total_recruitment'] > 0
+ ) {
+ siteBreakdownData = (
+ <>
+
+
+ Total recruitment per site
+
+
+
+
+
+ Biological sex breakdown by site
+
+
+
+ >
+ );
+ } else {
+ siteBreakdownData = (
+ There have been no candidates registered yet.
+ );
+ }
+ let projectBreakdownData = [];
+ for (const [key, value] of Object.entries(json['recruitment'])) {
+ if (key !== 'overall') {
+ projectBreakdownData.push(
+
+ {progressBarBuilder(value)}
+
+ );
+ }
+ }
+ setProjectBreakdown(projectBreakdownData);
+ setOverall(overallData);
+ setSiteBreakdown(siteBreakdownData);
+ setLoading(false);
+ }
+ }, [props.data]);
+
+ /**
+ * progressBarBuilder - generates the graph content.
+ *
+ * @param {object} data - data needed to generate the graph content.
+ * @return {JSX.Element} the charts to render to the widget panel.
+ */
+ const progressBarBuilder = (data) => {
+ let title;
+ let content;
+ if (data['recruitment_target']) {
+ title =
+ {data['title']}
+
;
+ if (data['surpassed_recruitment']) {
+ content = (
+
+
+ The recruitment target (
+ {data['recruitment_target']}
+ ) has been passed.
+
+
+
+
+ {data['female_total']}
Females
+
+
+
+
+ {data['male_total']}
Males
+
+
+
+ Target: {data['recruitment_target']}
+
+
+
+ );
+ } else {
+ content = (
+
+
+
+ {data['female_total']}
Females
+
+
+
+
+ {data['male_total']}
Males
+
+
+
+ Target: {data['recruitment_target']}
+
+
+ );
+ }
+ } else {
+ content = (
+
+ Please add a recruitment target for {data['title']}.
+
+ );
+ }
+ return (
+ <>
+ {title}
+ {content}
+ >
+ );
+ };
+
+ /**
+ * Renders the React component.
+ *
+ * @return {JSX.Element} - React markup for component.
+ */
+ return loading ? : (
+
+ {overall}
+ >,
+ title: 'Recruitment - overall',
+ },
+ {
+ content:
+ <>
+ {siteBreakdown}
+ >,
+ title: 'Recruitment - site breakdown',
+ },
+ {
+ content:
+ <>
+ {projectBreakdown}
+ >,
+ title: 'Recruitment - project breakdown',
+ },
+ ]}
+ />
+ );
+};
+Recruitment.propTypes = {
+ data: PropTypes.object,
+};
+Recruitment.defaultProps = {
+ data: {},
+};
+
+export default Recruitment;
diff --git a/modules/statistics/jsx/widgets/studyprogression.js b/modules/statistics/jsx/widgets/studyprogression.js
new file mode 100644
index 00000000000..8da62572a7b
--- /dev/null
+++ b/modules/statistics/jsx/widgets/studyprogression.js
@@ -0,0 +1,91 @@
+import React, {useEffect, useState} from 'react';
+import PropTypes from 'prop-types';
+import Loader from 'Loader';
+import Panel from 'jsx/Panel';
+
+/**
+ * StudyProgression - a widget containing statistics for study data.
+ *
+ * @param {object} props
+ * @return {JSX.Element}
+ */
+const StudyProgression = (props) => {
+ const [loading, setLoading] = useState(true);
+ const [siteScans, setSiteScans] = useState({});
+ const [siteRecruitments, setSiteRecruitments] = useState({});
+
+ /**
+ * useEffect - modified to run when props.data updates.
+ */
+ useEffect(() => {
+ const json = props.data;
+ if (json && Object.keys(json).length !== 0) {
+ setSiteScans(
+ json['studyprogression']['total_scans'] > 0
+ ?
+
Scan sessions per site
+
+
+
+ Note that the Recruitment and Study Progression charts
+ include data from ineligible, excluded, and consent
+ withdrawn candidates.
+
+
+ : There have been no scans yet.
+ );
+ setSiteRecruitments(
+ json['studyprogression']['recruitment']['overall']
+ ['total_recruitment'] > 0
+ ?
+
Recruitment per site
+
+
+
+ Note that the Recruitment and Study Progression charts
+ include data from ineligible, excluded, and consent
+ withdrawn candidates.
+
+
+ : There have been no candidates registered yet.
+ );
+ setLoading(false);
+ }
+ }, [props.data]);
+
+ /**
+ * Renders the React component.
+ *
+ * @return {JSX.Element} - React markup for component.
+ */
+ return loading ? : (
+
+ {siteScans}
+ >,
+ title: 'Study Progression - site scans',
+ },
+ {
+ content: <>
+ {siteRecruitments}
+ >,
+ title: 'Study Progression - site recruitment',
+ },
+ ]}
+ />
+ );
+};
+StudyProgression.propTypes = {
+ data: PropTypes.object,
+};
+StudyProgression.defaultProps = {
+ data: {},
+};
+
+export default StudyProgression;
diff --git a/modules/statistics/php/module.class.inc b/modules/statistics/php/module.class.inc
index bc1186b5c24..d1486a3d861 100644
--- a/modules/statistics/php/module.class.inc
+++ b/modules/statistics/php/module.class.inc
@@ -73,257 +73,33 @@ class Module extends \Module
switch ($type) {
case 'dashboard':
$factory = \NDB_Factory::singleton();
- $config = $factory->config();
- $db = $factory->database();
$baseURL = $factory->settings()->getBaseURL();
-
- $recruitmentTarget = $config->getSetting('recruitmentTarget');
-
- $totalScans = $this->_getTotalRecruitment($db);
- $recruitment = [
- 'overall' => $this->_createProjectProgressBar(
- 'overall',
- "Overall Recruitment",
- $recruitmentTarget,
- $totalScans,
- $db,
- )
- ];
-
- $projects = \Utility::getProjectList();
- foreach (array_keys($projects) as $projectID) {
- $projectInfo = $config->getProjectSettings($projectID);
- if (is_null($projectInfo)) {
- throw new \LorisException(
- 'No project settings exist in the Database for ' .
- 'project ID ' . intval($projectID)
- );
- }
- $recruitment[$projectID] = $this->_createProjectProgressBar(
- $projectID,
- $projectInfo['Name'],
- $projectInfo['recruitmentTarget'],
- $this->getTotalRecruitmentByProject($db, $projectID),
- $db
- );
- }
-
- // These are just here to avoid phpcs complaining about line length.
- $overallstring = 'View overall recruitment';
- $sitebreakdownstring = 'View site breakdown';
- $projbreakdownstring = 'View project breakdown';
-
- $recpersite = 'View recruitment per site';
-
- return [
- new \LORIS\dashboard\Widget(
- new \LORIS\dashboard\WidgetContent(
- "Recruitment",
- $this->renderTemplate(
- "recruitment.tpl",
- [
- 'recruitment' => $recruitment
- ]
- ),
- "",
- ),
- new \LORIS\dashboard\WidgetDisplayProps(
- "normal",
- 10,
- [
- 'overall-recruitment' => $overallstring,
- 'recruitment-site-breakdown' => $sitebreakdownstring,
- 'recruitment-project-breakdown' => $projbreakdownstring,
- ]
- ),
- new \LORIS\dashboard\WidgetDependencies(
- [
- $baseURL . "/css/c3.css",
- $baseURL . "/statistics/css/recruitment.css"
- ],
- [
- $baseURL. '/js/d3.min.js',
- $baseURL . '/js/c3.min.js',
- $baseURL . "/statistics/js/recruitment.js"
- ],
- )
+ $widget = new \LORIS\dashboard\Widget(
+ new \LORIS\dashboard\WidgetContent(
+ '',
+ '',
+ '',
),
- new \LORIS\dashboard\Widget(
- new \LORIS\dashboard\WidgetContent(
- "Study Progression",
- $this->renderTemplate(
- "studyprogression.tpl",
- [
- 'total_scans' => $totalScans,
- 'recruitment' => $recruitment
- ]
- ),
- "",
- ),
- new \LORIS\dashboard\WidgetDisplayProps(
- "normal",
- 10,
- [
- 'scan-line-chart-panel' => 'View scans per site',
- 'recruitment-line-chart-panel' => $recpersite
- ]
- ),
- new \LORIS\dashboard\WidgetDependencies(
- [
- $baseURL . "/css/c3.css"
- ],
- [
- $baseURL . '/js/d3.min.js',
- $baseURL . '/js/c3.min.js',
- $baseURL . "/statistics/js/studyprogression.js"
- ],
- )
+ new \LORIS\dashboard\WidgetDisplayProps(
+ 'normal',
+ 10,
+ []
+ ),
+ new \LORIS\dashboard\WidgetDependencies(
+ [
+ $baseURL . '/css/c3.css',
+ $baseURL . '/statistics/css/recruitment.css'
+ ],
+ [
+ $baseURL . '/statistics/js/WidgetIndex.js'
+ ],
)
- ];
- }
- return [];
- }
-
- /**
- * Gets the total count of candidates associated with a specific project
- *
- * @param \Database $DB The database connection to get the count from.
- *
- * @return int
- */
- private function _getTotalRecruitment(\Database $DB): int
- {
- return $DB->pselectOneInt(
- "SELECT COUNT(*) FROM candidate c
- WHERE c.Active='Y' AND c.Entity_type='Human'
- AND c.RegistrationCenterID <> 1",
- []
- ) ?? 0;
- }
+ );
+ $widget->setTemplate('div.tpl');
+ $widget->setTemplateVariables(['id' => 'statistics_widgets']);
- /**
- * Generates the template data for a progress bar.
- *
- * @param string $ID The name of the progress bar being
- * created.
- * @param string $title The title to add to the template
- * variables.
- * @param int $recruitmentTarget The target for this recruitment type.
- * @param int $totalRecruitment The total number of candidates of all
- * types.
- * @param \Database $db The database connection to get data from.
- *
- * @return array Smarty template data
- */
- private function _createProjectProgressBar(
- $ID,
- $title,
- $recruitmentTarget,
- $totalRecruitment,
- \Database $db
- ): array {
- $rv = [
- 'total_recruitment' => $totalRecruitment,
- 'title' => $title,
- ];
- if (empty($recruitmentTarget)) {
- return $rv;
- }
-
- $rv['recruitment_target'] = $recruitmentTarget;
- if ($ID == 'overall') {
- $totalFemales = $this->_getTotalSex($db, "Female");
- } else {
- $totalFemales = $this->getTotalSexByProject($db, "Female", intval($ID));
- }
- $rv['female_total'] = $totalFemales;
- $rv['female_percent']
- = round($totalFemales / $recruitmentTarget * 100);
- if ($ID == 'overall') {
- $totalMales = $this->_getTotalSex($db, "Male");
- } else {
- $totalMales = $this->getTotalSexByProject($db, "Male", intval($ID));
+ return [$widget];
}
- $rv['male_total'] = $totalMales;
- $rv['male_percent']
- = round($totalMales / $recruitmentTarget * 100);
- if ($totalRecruitment > $recruitmentTarget) {
- $rv['surpassed_recruitment'] = "true";
-
- $rv['female_full_percent']
- = round($totalFemales / $totalRecruitment * 100);
-
- $rv['male_full_percent']
- = round($totalMales / $totalRecruitment * 100);
- }
- return $rv;
- }
-
- /**
- * Gets the total count of candidates of a specific sex
- *
- * @param \Database $db A database connection to retrieve information
- * from.
- * @param string $sex Biological sex (male or female)
- *
- * @return int
- */
- private function _getTotalSex(\Database $db, string $sex) : int
- {
- return $db->pselectOneInt(
- "SELECT COUNT(c.CandID)
- FROM candidate c
- WHERE c.Sex=:sex AND c.Active='Y' AND c.Entity_type='Human'
- AND c.RegistrationCenterID <> 1",
- ['sex' => $sex]
- ) ?? 0;
- }
-
- /**
- * Gets the total count of candidates of a specific sex,
- * associated with a specific project
- *
- * @param \Database $DB A database connection to retrieve information
- * from.
- * @param string $sex A biological sex (male or female)
- * @param int $projectID Project ID
- *
- * @return int
- */
- function getTotalSexByProject(\Database $DB, string $sex, int $projectID) : int
- {
- return $DB->pselectOneInt(
- "SELECT COUNT(c.CandID)
- FROM candidate c
- WHERE c.Sex=:sex AND c.Active='Y' AND c.RegistrationProjectID=:PID
- AND c.Entity_type='Human' AND c.RegistrationCenterID <> 1",
- [
- 'sex' => $sex,
- 'PID' => "$projectID",
- ]
- ) ?? 0;
- }
-
-
- /**
- * Gets the total count of candidates associated with a specific project.
- *
- * @param \Database $db A database connection to retrieve information
- * from.
- * @param int $projectID The Project ID to get recruitment for.
- *
- * @return int
- */
- function getTotalRecruitmentByProject(\Database $db, int $projectID): int
- {
- return $db->pselectOneInt(
- "SELECT COUNT(*)
- FROM candidate c
- WHERE c.Active='Y'
- AND c.RegistrationProjectID=:PID
- AND c.Entity_type='Human'
- AND c.RegistrationCenterID <> 1",
- ['PID' => "$projectID"]
- ) ?? 0;
+ return [];
}
}
diff --git a/modules/statistics/php/widgets.class.inc b/modules/statistics/php/widgets.class.inc
new file mode 100644
index 00000000000..9afb6d07466
--- /dev/null
+++ b/modules/statistics/php/widgets.class.inc
@@ -0,0 +1,275 @@
+
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+class Widgets extends \NDB_Page implements ETagCalculator
+{
+ /**
+ * A cache of the endpoint results, so that it doesn't need to be
+ * recalculated for the ETag and handler.
+ */
+ private $_cache;
+
+ /**
+ * The handle function will return a json response.
+ *
+ * @param ServerRequestInterface $request The incoming PSR7 request
+ *
+ * @return ResponseInterface The outgoing PSR7 response
+ */
+ public function handle(ServerRequestInterface $request) : ResponseInterface
+ {
+ switch ($request->getMethod()) {
+ case 'GET':
+ $this->_cache = $this->_handleGET($request);
+ break;
+ default:
+ return new \LORIS\Http\Response\JSON\MethodNotAllowed(
+ $this->allowedMethods()
+ );
+ }
+ return $this->_cache;
+ }
+
+ /**
+ * Handles the retrieval of statistical data for the request.
+ *
+ * @param ServerRequestInterface $request The incoming PSR7 request.
+ *
+ * @return ResponseInterface The outgoing PSR7 response
+ */
+ private function _handleGET(ServerRequestInterface $request)
+ {
+ if (isset($this->_cache)) {
+ return $this->_cache;
+ }
+ $factory = \NDB_Factory::singleton();
+ $config = $factory->config();
+ $db = $request->getAttribute('loris')->getDatabaseConnection();
+
+ $recruitmentTarget = $config->getSetting('recruitmentTarget');
+
+ $totalScans = $this->_getTotalRecruitment($db);
+ $recruitment = [
+ 'overall' => $this->_createProjectProgressBar(
+ 'overall',
+ "Overall Recruitment",
+ $recruitmentTarget,
+ $totalScans,
+ $db,
+ )
+ ];
+
+ $projects = \Utility::getProjectList();
+ foreach (array_keys($projects) as $projectID) {
+ $projectInfo = $config->getProjectSettings($projectID);
+ if (is_null($projectInfo)) {
+ throw new \LorisException(
+ 'No project settings exist in the Database for ' .
+ 'project ID ' . intval($projectID)
+ );
+ }
+ $recruitment[$projectID] = $this->_createProjectProgressBar(
+ $projectID,
+ $projectInfo['Name'],
+ $projectInfo['recruitmentTarget'],
+ $this->getTotalRecruitmentByProject($db, $projectID),
+ $db
+ );
+ }
+
+ $values = [];
+ // Used for the react widget recruitment.js
+ $values['recruitment'] = $recruitment;
+ // Used for the react widget studyprogression.js
+ $values['studyprogression'] = [
+ 'total_scans' => $totalScans,
+ 'recruitment' => $recruitment
+ ];
+
+ $this->_cache = new \LORIS\Http\Response\JsonResponse($values);
+
+ return $this->_cache;
+ }
+
+ /**
+ * Gets the total count of candidates associated with a specific project
+ *
+ * @param \Database $DB The database connection to get the count from.
+ *
+ * @return int
+ */
+ private function _getTotalRecruitment(\Database $DB): int
+ {
+ return $DB->pselectOneInt(
+ "SELECT COUNT(*) FROM candidate c
+ WHERE c.Active='Y' AND c.Entity_type='Human'
+ AND c.RegistrationCenterID <> 1",
+ []
+ ) ?? 0;
+ }
+
+ /**
+ * Generates the template data for a progress bar.
+ *
+ * @param string $ID The name of the progress bar being
+ * created.
+ * @param string $title The title to add to the template
+ * variables.
+ * @param int $recruitmentTarget The target for this recruitment type.
+ * @param int $totalRecruitment The total number of candidates of all
+ * types.
+ * @param \Database $db The database connection to get data from.
+ *
+ * @return array Smarty template data
+ */
+ private function _createProjectProgressBar(
+ $ID,
+ $title,
+ $recruitmentTarget,
+ $totalRecruitment,
+ \Database $db
+ ): array {
+ $rv = [
+ 'total_recruitment' => $totalRecruitment,
+ 'title' => $title,
+ ];
+ if (empty($recruitmentTarget)) {
+ return $rv;
+ }
+
+ $rv['recruitment_target'] = $recruitmentTarget;
+ if ($ID == 'overall') {
+ $totalFemales = $this->_getTotalSex($db, "Female");
+ } else {
+ $totalFemales = $this->getTotalSexByProject($db, "Female", intval($ID));
+ }
+ $rv['female_total'] = $totalFemales;
+ $rv['female_percent']
+ = round($totalFemales / $recruitmentTarget * 100);
+ if ($ID == 'overall') {
+ $totalMales = $this->_getTotalSex($db, "Male");
+ } else {
+ $totalMales = $this->getTotalSexByProject($db, "Male", intval($ID));
+ }
+ $rv['male_total'] = $totalMales;
+ $rv['male_percent']
+ = round($totalMales / $recruitmentTarget * 100);
+ if ($totalRecruitment > $recruitmentTarget) {
+ $rv['surpassed_recruitment'] = "true";
+
+ $rv['female_full_percent']
+ = round($totalFemales / $totalRecruitment * 100);
+
+ $rv['male_full_percent']
+ = round($totalMales / $totalRecruitment * 100);
+ }
+ return $rv;
+ }
+
+ /**
+ * Gets the total count of candidates of a specific sex
+ *
+ * @param \Database $db A database connection to retrieve information
+ * from.
+ * @param string $sex Biological sex (male or female)
+ *
+ * @return int
+ */
+ private function _getTotalSex(\Database $db, string $sex) : int
+ {
+ return $db->pselectOneInt(
+ "SELECT COUNT(c.CandID)
+ FROM candidate c
+ WHERE c.Sex=:sex AND c.Active='Y' AND c.Entity_type='Human'
+ AND c.RegistrationCenterID <> 1",
+ ['sex' => $sex]
+ ) ?? 0;
+ }
+
+ /**
+ * Gets the total count of candidates of a specific sex,
+ * associated with a specific project
+ *
+ * @param \Database $DB A database connection to retrieve information
+ * from.
+ * @param string $sex A biological sex (male or female)
+ * @param int $projectID Project ID
+ *
+ * @return int
+ */
+ function getTotalSexByProject(\Database $DB, string $sex, int $projectID) : int
+ {
+ return $DB->pselectOneInt(
+ "SELECT COUNT(c.CandID)
+ FROM candidate c
+ WHERE c.Sex=:sex AND c.Active='Y' AND c.RegistrationProjectID=:PID
+ AND c.Entity_type='Human' AND c.RegistrationCenterID <> 1",
+ [
+ 'sex' => $sex,
+ 'PID' => "$projectID",
+ ]
+ ) ?? 0;
+ }
+
+ /**
+ * Gets the total count of candidates associated with a specific project.
+ *
+ * @param \Database $db A database connection to retrieve information
+ * from.
+ * @param int $projectID The Project ID to get recruitment for.
+ *
+ * @return int
+ */
+ function getTotalRecruitmentByProject(\Database $db, int $projectID): int
+ {
+ return $db->pselectOneInt(
+ "SELECT COUNT(*)
+ FROM candidate c
+ WHERE c.Active='Y'
+ AND c.RegistrationProjectID=:PID
+ AND c.Entity_type='Human'
+ AND c.RegistrationCenterID <> 1",
+ ['PID' => "$projectID"]
+ ) ?? 0;
+ }
+
+ /**
+ * The ETagCalculator provides the ability to calculate
+ * an ETag for an incoming HTTP request.
+ *
+ * @param ServerRequestInterface $request The incoming PSR7 request.
+ *
+ * @return string The value to use for the ETag header.
+ */
+ public function ETag(ServerRequestInterface $request): string
+ {
+ return md5(json_encode($this->_handleGET($request)->getBody()));
+ }
+
+ /**
+ * Return an array of valid HTTP methods for this endpoint.
+ *
+ * @return string[] Valid versions
+ */
+ protected function allowedMethods(): array
+ {
+ return ['GET'];
+ }
+}
diff --git a/modules/statistics/templates/progress_bar.tpl b/modules/statistics/templates/progress_bar.tpl
deleted file mode 100644
index f5a822a7ac5..00000000000
--- a/modules/statistics/templates/progress_bar.tpl
+++ /dev/null
@@ -1,43 +0,0 @@
- {if $project['recruitment_target']|default neq ""}
- {$project['title']}
- {if $project['surpassed_recruitment']|default eq "true"}
- The recruitment target ({$project['recruitment_target']}) has been passed.
-
-
-
- {$project['female_total']}
-
- Females
-
-
-
-
- {$project['male_total']}
-
- Males
-
-
-
Target: {$project['recruitment_target']}
-
- {else}
-
-
-
- {$project['female_total']}
-
- Females
-
-
-
-
- {$project['male_total']}
-
- Males
-
-
-
Target: {$project['recruitment_target']}
-
- {/if}
-{else}
- Please add a recruitment target for {$project['title']}.
-{/if}
diff --git a/modules/statistics/templates/recruitment.tpl b/modules/statistics/templates/recruitment.tpl
deleted file mode 100644
index 2ef76e3ce05..00000000000
--- a/modules/statistics/templates/recruitment.tpl
+++ /dev/null
@@ -1,28 +0,0 @@
-
- {include file='progress_bar.tpl' project=$recruitment["overall"]}
-
-
- {if $recruitment['overall']['total_recruitment'] neq 0}
-
-
-
Total recruitment per site
-
-
-
-
-
-
Biological sex breakdown by site
-
-
-
- {else}
-
There have been no candidates registered yet.
- {/if}
-
-
-{foreach from=$recruitment key=ID item=project}
- {if $ID != "overall"}
- {include file='progress_bar.tpl' project=$project}
- {/if}
-{/foreach}
-
diff --git a/modules/statistics/templates/studyprogression.tpl b/modules/statistics/templates/studyprogression.tpl
deleted file mode 100644
index 5cde6d2a56f..00000000000
--- a/modules/statistics/templates/studyprogression.tpl
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
Scan sessions per site
- {if $total_scans neq 0}
-
- {else}
-
There have been no scans yet.
- {/if}
-
-
-
Recruitment per site
- {if $recruitment['overall']['total_recruitment'] neq 0}
-
- {else}
-
There have been no candidates registered yet.
- {/if}
-
-Note that the Recruitment and Study Progression charts include data from ineligible, excluded, and consent withdrawn candidates.
diff --git a/package-lock.json b/package-lock.json
index 1d27faf2cea..5ace5ba5201 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -34,6 +34,8 @@
"@babel/eslint-parser": "^7.19.1",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.6.3",
+ "@types/c3": "^0.7.8",
+ "@types/d3": "^7.4.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@types/react-redux": "7.1.16",
@@ -41,9 +43,9 @@
"@typescript-eslint/parser": "^5.45.0",
"alex": ">=11.0.0",
"babel-loader": "^8.0.5",
- "c3": "^0.7.15",
+ "c3": "^0.7.20",
"css-loader": "^3.4.2",
- "d3": "^5.15.0",
+ "d3": "^5.16.0",
"eslint": "^7.17.0",
"eslint-config-google": "0.9.1",
"eslint-plugin-jsdoc": "^39.6.4",
@@ -2187,6 +2189,237 @@
"@types/estree": "*"
}
},
+ "node_modules/@types/c3": {
+ "version": "0.7.8",
+ "resolved": "https://registry.npmjs.org/@types/c3/-/c3-0.7.8.tgz",
+ "integrity": "sha512-qUhbhHIa7SzpDZVHTUx51XUKPzkG3xLHKZGhwvfIs5Fy3NSc8qtH8I1u6N3Dp44Ih54qyUMw6xTIiDuOUBanxA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3": "^4"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3": {
+ "version": "4.13.12",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-4.13.12.tgz",
+ "integrity": "sha512-/bbFtkOBc04gGGN8N9rMG5ps3T0eIj5I8bnYe9iIyeM5qoOrydPCbFYlEPUnj2h9ibc2i+QZfDam9jY5XTrTxQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-array": "^1",
+ "@types/d3-axis": "^1",
+ "@types/d3-brush": "^1",
+ "@types/d3-chord": "^1",
+ "@types/d3-collection": "*",
+ "@types/d3-color": "^1",
+ "@types/d3-dispatch": "^1",
+ "@types/d3-drag": "^1",
+ "@types/d3-dsv": "^1",
+ "@types/d3-ease": "^1",
+ "@types/d3-force": "^1",
+ "@types/d3-format": "^1",
+ "@types/d3-geo": "^1",
+ "@types/d3-hierarchy": "^1",
+ "@types/d3-interpolate": "^1",
+ "@types/d3-path": "^1",
+ "@types/d3-polygon": "^1",
+ "@types/d3-quadtree": "^1",
+ "@types/d3-queue": "*",
+ "@types/d3-random": "^1",
+ "@types/d3-request": "*",
+ "@types/d3-scale": "^1",
+ "@types/d3-selection": "^1",
+ "@types/d3-shape": "^1",
+ "@types/d3-time": "^1",
+ "@types/d3-time-format": "^2",
+ "@types/d3-timer": "^1",
+ "@types/d3-transition": "^1",
+ "@types/d3-voronoi": "*",
+ "@types/d3-zoom": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-array": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.9.tgz",
+ "integrity": "sha512-E/7RgPr2ylT5dWG0CswMi9NpFcjIEDqLcUSBgNHe/EMahfqYaTx4zhcggG3khqoEB/leY4Vl6nTSbwLUPjXceA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-axis": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.16.tgz",
+ "integrity": "sha512-p7085weOmo4W+DzlRRVC/7OI/jugaKbVa6WMQGCQscaMylcbuaVEGk7abJLNyGVFLeCBNrHTdDiqRGnzvL0nXQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-brush": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.1.5.tgz",
+ "integrity": "sha512-4zGkBafJf5zCsBtLtvDj/pNMo5X9+Ii/1hUz0GvQ+wEwelUBm2AbIDAzJnp2hLDFF307o0fhxmmocHclhXC+tw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-chord": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.11.tgz",
+ "integrity": "sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-color": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
+ "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-dispatch": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.9.tgz",
+ "integrity": "sha512-zJ44YgjqALmyps+II7b1mZLhrtfV/FOxw9owT87mrweGWcg+WK5oiJX2M3SYJ0XUAExBduarysfgbR11YxzojQ==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-drag": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.5.tgz",
+ "integrity": "sha512-7NeTnfolst1Js3Vs7myctBkmJWu6DMI3k597AaHUX98saHjHWJ6vouT83UrpE+xfbSceHV+8A0JgxuwgqgmqWw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-dsv": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.2.1.tgz",
+ "integrity": "sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-ease": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.11.tgz",
+ "integrity": "sha512-wUigPL0kleGZ9u3RhzBP07lxxkMcUjL5IODP42mN/05UNL+JJCDnpEPpFbJiPvLcTeRKGIRpBBJyP/1BNwYsVA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-force": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.4.tgz",
+ "integrity": "sha512-fkorLTKvt6AQbFBQwn4aq7h9rJ4c7ZVcPMGB8X6eFFveAyMZcv7t7m6wgF4Eg93rkPgPORU7sAho1QSHNcZu6w==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-format": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
+ "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-geo": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.12.3.tgz",
+ "integrity": "sha512-yZbPb7/5DyL/pXkeOmZ7L5ySpuGr4H48t1cuALjnJy5sXQqmSSAYBiwa6Ya/XpWKX2rJqGDDubmh3nOaopOpeA==",
+ "dev": true,
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-hierarchy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
+ "integrity": "sha512-AbStKxNyWiMDQPGDguG2Kuhlq1Sv539pZSxYbx4UZeYkutpPwXCcgyiRrlV4YH64nIOsKx7XVnOMy9O7rJsXkg==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-interpolate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz",
+ "integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-color": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-polygon": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.8.tgz",
+ "integrity": "sha512-1TOJPXCBJC9V3+K3tGbTqD/CsqLyv/YkTXAcwdsZzxqw5cvpdnCuDl42M4Dvi8XzMxZNCT9pL4ibrK2n4VmAcw==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-quadtree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.9.tgz",
+ "integrity": "sha512-5E0OJJn2QVavITFEc1AQlI8gLcIoDZcTKOD3feKFckQVmFV4CXhqRFt83tYNVNIN4ZzRkjlAMavJa1ldMhf5rA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-random": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.3.tgz",
+ "integrity": "sha512-XXR+ZbFCoOd4peXSMYJzwk0/elP37WWAzS/DG+90eilzVbUSsgKhBcWqylGWe+lA2ubgr7afWAOBaBxRgMUrBQ==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-scale": {
+ "version": "1.0.17",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-1.0.17.tgz",
+ "integrity": "sha512-baIP5/gw+PS8Axs1lfZCeIjcOXen/jxQmgFEjbYThwaj2drvivOIrJMh2Ig4MeenrogCH6zkhiOxCPRkvN1scA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-time": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-selection": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.3.tgz",
+ "integrity": "sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-shape": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
+ "integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-path": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-time": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.1.1.tgz",
+ "integrity": "sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-time-format": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
+ "integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-timer": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.10.tgz",
+ "integrity": "sha512-ZnAbquVqy+4ZjdW0cY6URp+qF/AzTVNda2jYyOzpR2cPT35FTXl78s15Bomph9+ckOiI1TtkljnWkwbIGAb6rg==",
+ "dev": true
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-transition": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.3.2.tgz",
+ "integrity": "sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "node_modules/@types/c3/node_modules/@types/d3-zoom": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.8.3.tgz",
+ "integrity": "sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-interpolate": "^1",
+ "@types/d3-selection": "^1"
+ }
+ },
"node_modules/@types/concat-stream": {
"version": "2.0.0",
"dev": true,
@@ -2195,6 +2428,292 @@
"@types/node": "*"
}
},
+ "node_modules/@types/d3": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
+ "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz",
+ "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.1.tgz",
+ "integrity": "sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.1.tgz",
+ "integrity": "sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-collection": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.10.tgz",
+ "integrity": "sha512-54Fdv8u5JbuXymtmXm2SYzi1x/Svt+jfWBU5junkhrCewL92VjqtCBDn97coBRVwVFmYNnVTNDyV8gQyPYfm+A==",
+ "dev": true
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.1.tgz",
+ "integrity": "sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
+ "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.1.tgz",
+ "integrity": "sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.0.tgz",
+ "integrity": "sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A==",
+ "dev": true
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
+ "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz",
+ "integrity": "sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
+ "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.2.tgz",
+ "integrity": "sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz",
+ "integrity": "sha512-g+sey7qrCa3UbsQlMZZBOHROkFqx7KZKvUpRzI/tAp/8erZWpYq7FgNKvYwebi2LaEiVs1klhUfd3WCThxmmWQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
+ "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz",
+ "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz",
+ "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-queue": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-queue/-/d3-queue-3.0.8.tgz",
+ "integrity": "sha512-1FWOiI/MYwS5Z1Sa9EvS1Xet3isiVIIX5ozD6iGnwHonGcqL+RcC1eThXN5VfDmAiYt9Me9EWNEv/9J9k9RIKQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-request": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-request/-/d3-request-1.0.6.tgz",
+ "integrity": "sha512-4nRKDUBg3EBx8VowpMvM3NAVMiMMI1qFUOYv3OJsclGjHX6xjtu09nsWhRQ0fvSUla3MEjb5Ch4IeaYarMEi1w==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-dsv": "^1"
+ }
+ },
+ "node_modules/@types/d3-request/node_modules/@types/d3-dsv": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.2.1.tgz",
+ "integrity": "sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.3.tgz",
+ "integrity": "sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
+ "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
+ "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
+ "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==",
+ "dev": true
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.2.tgz",
+ "integrity": "sha512-jo5o/Rf+/u6uerJ/963Dc39NI16FQzqwOc54bwvksGAdVfvDrqDpVeq95bEvPtBwLCVZutAEyAtmSyEMxN7vxQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-voronoi": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz",
+ "integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.1.tgz",
+ "integrity": "sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
"node_modules/@types/debug": {
"version": "4.1.7",
"dev": true,
@@ -2231,6 +2750,12 @@
"@types/estree": "*"
}
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.10",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
+ "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
+ "dev": true
+ },
"node_modules/@types/hast": {
"version": "2.3.4",
"dev": true,
@@ -13177,6 +13702,239 @@
"@types/estree": "*"
}
},
+ "@types/c3": {
+ "version": "0.7.8",
+ "resolved": "https://registry.npmjs.org/@types/c3/-/c3-0.7.8.tgz",
+ "integrity": "sha512-qUhbhHIa7SzpDZVHTUx51XUKPzkG3xLHKZGhwvfIs5Fy3NSc8qtH8I1u6N3Dp44Ih54qyUMw6xTIiDuOUBanxA==",
+ "dev": true,
+ "requires": {
+ "@types/d3": "^4"
+ },
+ "dependencies": {
+ "@types/d3": {
+ "version": "4.13.12",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-4.13.12.tgz",
+ "integrity": "sha512-/bbFtkOBc04gGGN8N9rMG5ps3T0eIj5I8bnYe9iIyeM5qoOrydPCbFYlEPUnj2h9ibc2i+QZfDam9jY5XTrTxQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "^1",
+ "@types/d3-axis": "^1",
+ "@types/d3-brush": "^1",
+ "@types/d3-chord": "^1",
+ "@types/d3-collection": "*",
+ "@types/d3-color": "^1",
+ "@types/d3-dispatch": "^1",
+ "@types/d3-drag": "^1",
+ "@types/d3-dsv": "^1",
+ "@types/d3-ease": "^1",
+ "@types/d3-force": "^1",
+ "@types/d3-format": "^1",
+ "@types/d3-geo": "^1",
+ "@types/d3-hierarchy": "^1",
+ "@types/d3-interpolate": "^1",
+ "@types/d3-path": "^1",
+ "@types/d3-polygon": "^1",
+ "@types/d3-quadtree": "^1",
+ "@types/d3-queue": "*",
+ "@types/d3-random": "^1",
+ "@types/d3-request": "*",
+ "@types/d3-scale": "^1",
+ "@types/d3-selection": "^1",
+ "@types/d3-shape": "^1",
+ "@types/d3-time": "^1",
+ "@types/d3-time-format": "^2",
+ "@types/d3-timer": "^1",
+ "@types/d3-transition": "^1",
+ "@types/d3-voronoi": "*",
+ "@types/d3-zoom": "^1"
+ }
+ },
+ "@types/d3-array": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.9.tgz",
+ "integrity": "sha512-E/7RgPr2ylT5dWG0CswMi9NpFcjIEDqLcUSBgNHe/EMahfqYaTx4zhcggG3khqoEB/leY4Vl6nTSbwLUPjXceA==",
+ "dev": true
+ },
+ "@types/d3-axis": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.16.tgz",
+ "integrity": "sha512-p7085weOmo4W+DzlRRVC/7OI/jugaKbVa6WMQGCQscaMylcbuaVEGk7abJLNyGVFLeCBNrHTdDiqRGnzvL0nXQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "@types/d3-brush": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.1.5.tgz",
+ "integrity": "sha512-4zGkBafJf5zCsBtLtvDj/pNMo5X9+Ii/1hUz0GvQ+wEwelUBm2AbIDAzJnp2hLDFF307o0fhxmmocHclhXC+tw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "@types/d3-chord": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.11.tgz",
+ "integrity": "sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg==",
+ "dev": true
+ },
+ "@types/d3-color": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
+ "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==",
+ "dev": true
+ },
+ "@types/d3-dispatch": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.9.tgz",
+ "integrity": "sha512-zJ44YgjqALmyps+II7b1mZLhrtfV/FOxw9owT87mrweGWcg+WK5oiJX2M3SYJ0XUAExBduarysfgbR11YxzojQ==",
+ "dev": true
+ },
+ "@types/d3-drag": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.5.tgz",
+ "integrity": "sha512-7NeTnfolst1Js3Vs7myctBkmJWu6DMI3k597AaHUX98saHjHWJ6vouT83UrpE+xfbSceHV+8A0JgxuwgqgmqWw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "@types/d3-dsv": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.2.1.tgz",
+ "integrity": "sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==",
+ "dev": true
+ },
+ "@types/d3-ease": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.11.tgz",
+ "integrity": "sha512-wUigPL0kleGZ9u3RhzBP07lxxkMcUjL5IODP42mN/05UNL+JJCDnpEPpFbJiPvLcTeRKGIRpBBJyP/1BNwYsVA==",
+ "dev": true
+ },
+ "@types/d3-force": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.4.tgz",
+ "integrity": "sha512-fkorLTKvt6AQbFBQwn4aq7h9rJ4c7ZVcPMGB8X6eFFveAyMZcv7t7m6wgF4Eg93rkPgPORU7sAho1QSHNcZu6w==",
+ "dev": true
+ },
+ "@types/d3-format": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
+ "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==",
+ "dev": true
+ },
+ "@types/d3-geo": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.12.3.tgz",
+ "integrity": "sha512-yZbPb7/5DyL/pXkeOmZ7L5ySpuGr4H48t1cuALjnJy5sXQqmSSAYBiwa6Ya/XpWKX2rJqGDDubmh3nOaopOpeA==",
+ "dev": true,
+ "requires": {
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-hierarchy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
+ "integrity": "sha512-AbStKxNyWiMDQPGDguG2Kuhlq1Sv539pZSxYbx4UZeYkutpPwXCcgyiRrlV4YH64nIOsKx7XVnOMy9O7rJsXkg==",
+ "dev": true
+ },
+ "@types/d3-interpolate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz",
+ "integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-color": "^1"
+ }
+ },
+ "@types/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==",
+ "dev": true
+ },
+ "@types/d3-polygon": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.8.tgz",
+ "integrity": "sha512-1TOJPXCBJC9V3+K3tGbTqD/CsqLyv/YkTXAcwdsZzxqw5cvpdnCuDl42M4Dvi8XzMxZNCT9pL4ibrK2n4VmAcw==",
+ "dev": true
+ },
+ "@types/d3-quadtree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.9.tgz",
+ "integrity": "sha512-5E0OJJn2QVavITFEc1AQlI8gLcIoDZcTKOD3feKFckQVmFV4CXhqRFt83tYNVNIN4ZzRkjlAMavJa1ldMhf5rA==",
+ "dev": true
+ },
+ "@types/d3-random": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.3.tgz",
+ "integrity": "sha512-XXR+ZbFCoOd4peXSMYJzwk0/elP37WWAzS/DG+90eilzVbUSsgKhBcWqylGWe+lA2ubgr7afWAOBaBxRgMUrBQ==",
+ "dev": true
+ },
+ "@types/d3-scale": {
+ "version": "1.0.17",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-1.0.17.tgz",
+ "integrity": "sha512-baIP5/gw+PS8Axs1lfZCeIjcOXen/jxQmgFEjbYThwaj2drvivOIrJMh2Ig4MeenrogCH6zkhiOxCPRkvN1scA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-time": "^1"
+ }
+ },
+ "@types/d3-selection": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.3.tgz",
+ "integrity": "sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==",
+ "dev": true
+ },
+ "@types/d3-shape": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
+ "integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-path": "^1"
+ }
+ },
+ "@types/d3-time": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.1.1.tgz",
+ "integrity": "sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw==",
+ "dev": true
+ },
+ "@types/d3-time-format": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.3.1.tgz",
+ "integrity": "sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA==",
+ "dev": true
+ },
+ "@types/d3-timer": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.10.tgz",
+ "integrity": "sha512-ZnAbquVqy+4ZjdW0cY6URp+qF/AzTVNda2jYyOzpR2cPT35FTXl78s15Bomph9+ckOiI1TtkljnWkwbIGAb6rg==",
+ "dev": true
+ },
+ "@types/d3-transition": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.3.2.tgz",
+ "integrity": "sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "^1"
+ }
+ },
+ "@types/d3-zoom": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.8.3.tgz",
+ "integrity": "sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==",
+ "dev": true,
+ "requires": {
+ "@types/d3-interpolate": "^1",
+ "@types/d3-selection": "^1"
+ }
+ }
+ }
+ },
"@types/concat-stream": {
"version": "2.0.0",
"dev": true,
@@ -13184,6 +13942,294 @@
"@types/node": "*"
}
},
+ "@types/d3": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
+ "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "@types/d3-array": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz",
+ "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==",
+ "dev": true
+ },
+ "@types/d3-axis": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.1.tgz",
+ "integrity": "sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-brush": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.1.tgz",
+ "integrity": "sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw==",
+ "dev": true
+ },
+ "@types/d3-collection": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.10.tgz",
+ "integrity": "sha512-54Fdv8u5JbuXymtmXm2SYzi1x/Svt+jfWBU5junkhrCewL92VjqtCBDn97coBRVwVFmYNnVTNDyV8gQyPYfm+A==",
+ "dev": true
+ },
+ "@types/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==",
+ "dev": true
+ },
+ "@types/d3-contour": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.1.tgz",
+ "integrity": "sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-delaunay": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
+ "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
+ "dev": true
+ },
+ "@types/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw==",
+ "dev": true
+ },
+ "@types/d3-drag": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.1.tgz",
+ "integrity": "sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-dsv": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.0.tgz",
+ "integrity": "sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A==",
+ "dev": true
+ },
+ "@types/d3-ease": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
+ "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==",
+ "dev": true
+ },
+ "@types/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "@types/d3-force": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz",
+ "integrity": "sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA==",
+ "dev": true
+ },
+ "@types/d3-format": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
+ "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
+ "dev": true
+ },
+ "@types/d3-geo": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.2.tgz",
+ "integrity": "sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ==",
+ "dev": true,
+ "requires": {
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-hierarchy": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz",
+ "integrity": "sha512-g+sey7qrCa3UbsQlMZZBOHROkFqx7KZKvUpRzI/tAp/8erZWpYq7FgNKvYwebi2LaEiVs1klhUfd3WCThxmmWQ==",
+ "dev": true
+ },
+ "@types/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-color": "*"
+ }
+ },
+ "@types/d3-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
+ "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==",
+ "dev": true
+ },
+ "@types/d3-polygon": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz",
+ "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==",
+ "dev": true
+ },
+ "@types/d3-quadtree": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz",
+ "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==",
+ "dev": true
+ },
+ "@types/d3-queue": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-queue/-/d3-queue-3.0.8.tgz",
+ "integrity": "sha512-1FWOiI/MYwS5Z1Sa9EvS1Xet3isiVIIX5ozD6iGnwHonGcqL+RcC1eThXN5VfDmAiYt9Me9EWNEv/9J9k9RIKQ==",
+ "dev": true
+ },
+ "@types/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==",
+ "dev": true
+ },
+ "@types/d3-request": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-request/-/d3-request-1.0.6.tgz",
+ "integrity": "sha512-4nRKDUBg3EBx8VowpMvM3NAVMiMMI1qFUOYv3OJsclGjHX6xjtu09nsWhRQ0fvSUla3MEjb5Ch4IeaYarMEi1w==",
+ "dev": true,
+ "requires": {
+ "@types/d3-dsv": "^1"
+ },
+ "dependencies": {
+ "@types/d3-dsv": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.2.1.tgz",
+ "integrity": "sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==",
+ "dev": true
+ }
+ }
+ },
+ "@types/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-time": "*"
+ }
+ },
+ "@types/d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==",
+ "dev": true
+ },
+ "@types/d3-selection": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.3.tgz",
+ "integrity": "sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA==",
+ "dev": true
+ },
+ "@types/d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-path": "*"
+ }
+ },
+ "@types/d3-time": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
+ "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==",
+ "dev": true
+ },
+ "@types/d3-time-format": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
+ "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==",
+ "dev": true
+ },
+ "@types/d3-timer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
+ "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==",
+ "dev": true
+ },
+ "@types/d3-transition": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.2.tgz",
+ "integrity": "sha512-jo5o/Rf+/u6uerJ/963Dc39NI16FQzqwOc54bwvksGAdVfvDrqDpVeq95bEvPtBwLCVZutAEyAtmSyEMxN7vxQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-voronoi": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz",
+ "integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==",
+ "dev": true
+ },
+ "@types/d3-zoom": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.1.tgz",
+ "integrity": "sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
"@types/debug": {
"version": "4.1.7",
"dev": true,
@@ -13215,6 +14261,12 @@
"@types/estree": "*"
}
},
+ "@types/geojson": {
+ "version": "7946.0.10",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
+ "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
+ "dev": true
+ },
"@types/hast": {
"version": "2.3.4",
"dev": true,
diff --git a/package.json b/package.json
index 67a12b90dce..ee1b42c2d9e 100644
--- a/package.json
+++ b/package.json
@@ -33,14 +33,16 @@
"@babel/preset-react": "^7.6.3",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
+ "@types/c3": "^0.7.8",
+ "@types/d3": "^7.4.0",
"@types/react-redux": "7.1.16",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"alex": ">=11.0.0",
"babel-loader": "^8.0.5",
- "c3": "^0.7.15",
+ "c3": "^0.7.20",
"css-loader": "^3.4.2",
- "d3": "^5.15.0",
+ "d3": "^5.16.0",
"eslint": "^7.17.0",
"eslint-config-google": "0.9.1",
"eslint-plugin-jsdoc": "^39.6.4",
diff --git a/webpack.config.js b/webpack.config.js
index 733a4d050b8..558451669ac 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -314,6 +314,7 @@ const lorisModules = {
module_manager: ['modulemanager'],
imaging_qc: ['imagingQCIndex'],
server_processes_manager: ['server_processes_managerIndex'],
+ statistics: ['WidgetIndex'],
instruments: ['CandidateInstrumentList'],
candidate_profile: ['CandidateInfo'],
api_docs: ['swagger-ui_custom'],