diff --git a/frontend/package.json b/frontend/package.json index 0a6781b..6564cad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,11 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", + "@visx/axis": "^3.1.0", + "@visx/curve": "^3.0.0", + "@visx/group": "^3.0.0", + "@visx/scale": "^3.0.0", + "@visx/shape": "^3.0.0", "axios": "1.2.1", "date-fns": "2.29.3", "ejs": "3.1.8", diff --git a/frontend/src/Call/Activity.js b/frontend/src/Call/Activity.js new file mode 100644 index 0000000..a000c06 --- /dev/null +++ b/frontend/src/Call/Activity.js @@ -0,0 +1,69 @@ +import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; +import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'; +import FilterModal from "./components/FilterModal"; +import GroupModal from "./components/GroupModal"; +import CalendarModal from "./components/CalendarModal"; +import CallPlayer from "./CallPlayer"; + +import { useSelector, useDispatch } from 'react-redux' +import { setFilter, setDateFilter } from "../features/callPlayer/callPlayerSlice"; + +import { useGetStatsQuery,useGetTalkgroupsQuery } from '../features/api/apiSlice' +import ActivityChart from "./ActivityChart"; +import { selectSystem } from "../features/systems/systemsSlice"; +import { + Container, + Label, + Rail, + Sticky, + Menu, + Icon, + Sidebar, + Header + +} from "semantic-ui-react"; +import "./CallPlayer.css"; +import queryString from '../query-string'; +import io from 'socket.io-client'; +import { useCallLink } from "./components/CallLinks"; + + +function Activity(props) { + const { shortName } = useParams(); + const { data: statsData, isSuccess: isStatsSuccess } = useGetStatsQuery(shortName); + const { data: talkgroupData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); + + const talkgroupStats = useMemo(() => { + if (statsData ) { + const result =[] + for (const tgNum in statsData.talkgroupStats) { + let tg = tgNum; + if (talkgroupData && talkgroupData.talkgroups[tgNum] !== undefined) { + tg = talkgroupData.talkgroups[tgNum].description; + } + result.push(); + } + return result; + } else { + return []; + } + + + }, [statsData, talkgroupData]); + + + return ( + +
Talkgroup Activity
+ { + talkgroupStats.map( chart => { + return chart + }) + } + +
+ ) + +} + +export default Activity; \ No newline at end of file diff --git a/frontend/src/Call/ActivityChart.js b/frontend/src/Call/ActivityChart.js new file mode 100644 index 0000000..508cc01 --- /dev/null +++ b/frontend/src/Call/ActivityChart.js @@ -0,0 +1,102 @@ +import React from "react"; +import { Group } from '@visx/group'; +import { curveStep} from '@visx/curve'; +import { LinePath } from '@visx/shape'; +import { scaleLinear, scaleTime } from '@visx/scale'; +import { timeParse, timeFormat } from 'd3-time-format'; +import { AxisBottom, AxisLeft } from '@visx/axis'; +import { Loader } from "semantic-ui-react"; +import { + Header +} from "semantic-ui-react"; + +function ActivityChart(props) { + + if (!props.data) { + return ( + Loading + ) + } + + var callTotals = []; + + + const now = new Date(); + var maxDate = now; + var minDate = now; + var minValue = 0; + var maxValue = 0; + var MS_PER_MINUTE = 60000; + + for (let j = 0; j < props.data.length; j++) { + let spotsBack = j;//statistic.callTotals.length - j; + let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); + if (time < minDate) minDate = time; + if (props.data[j] > maxValue) + maxValue = props.data[j]; + callTotals.push({ y: props.data[j], x: time }); + } + + + + + + + // Define the graph dimensions and margins + const width = 500; //props.width; + const height = 250; + const margin = { top: 20, bottom: 40, left: 40, right: 20 }; + const parseDate = timeParse("%Y%m%d"); + const format = timeFormat("%b %d"); + const formatDate = (date) => format(parseDate(date)); + const parseTime = timeParse('%Y-%m-%dT%H:%M:%S.%LZ'); + + // Then we'll create some bounds + const xMax = width - margin.left - margin.right; + const yMax = height - margin.top - margin.bottom; + + var xScale, yScale; + + // We'll use some mock data from `@vx/mock-data` for this. + // scales + xScale = scaleTime({ + range: [0, xMax], + domain: [minDate, maxDate], + tickFormat: () => (val) => formatDate(val) + }); + yScale = scaleLinear({ + range: [yMax, 0], + domain: [0, maxValue], + }); + + const handleClick = (event) => { + console.log('You clicked on:', event); + }; + // accessors + const x = d => xScale(d.x); + const y = d => yScale(d.y); + return ( +
+
{props.tg}
+ + + 520 ? 10 : 5} /> + + + + +
+ ); +} + +export default ActivityChart; + + diff --git a/frontend/src/Call/Calls.js b/frontend/src/Call/Calls.js index 703386e..02a64a8 100644 --- a/frontend/src/Call/Calls.js +++ b/frontend/src/Call/Calls.js @@ -1,19 +1,17 @@ -import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; +import React, { useEffect, useLayoutEffect, useState, useRef, useMemo } from "react"; import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'; import FilterModal from "./components/FilterModal"; import GroupModal from "./components/GroupModal"; import CalendarModal from "./components/CalendarModal"; import CallPlayer from "./CallPlayer"; +import Activity from "./Activity"; import { useSelector, useDispatch } from 'react-redux' import { setFilter, setDateFilter } from "../features/callPlayer/callPlayerSlice"; import { getCalls, addCall, getOlderCalls, getNewerCalls } from "../features/calls/callsSlice"; import { useGetGroupsQuery, useGetSystemsQuery, } from '../features/api/apiSlice' -import { selectSystem } from "../features/systems/systemsSlice"; import { Container, Label, - Rail, - Sticky, Menu, Icon, Sidebar @@ -21,7 +19,6 @@ import { import "./CallPlayer.css"; import queryString from '../query-string'; import io from 'socket.io-client'; -import { useCallLink } from "./components/CallLinks"; @@ -43,6 +40,7 @@ function Calls(props) { const [filterVisible, setFilterVisible] = useState(false); const [groupVisible, setGroupVisible] = useState(false); const [calendarVisible, setCalendarVisible] = useState(false); + const [activityVisible, setActivityVisible] = useState(false); const [isConnected, setIsConnected] = useState(socket.connected); const [selectCallId, setSelectCallId] = useState(false); const [live, setLive] = useState(true); @@ -85,6 +83,7 @@ function Calls(props) { const handleSidebarToggle = () => setSidebarOpened(!sidebarOpened); const handleFilterToggle = () => setFilterVisible(!filterVisible); const handleCalendarToggle = () => setCalendarVisible(!calendarVisible); + const handleActivityToggle = () => setActivityVisible(!activityVisible); const getFilterDescription = () => { let filter = { type: 'all', code: "", filterStarred: false }; @@ -400,6 +399,10 @@ function Calls(props) { {system && system.name} + + + Activity + @@ -416,7 +419,10 @@ function Calls(props) { - + {activityVisible + ? + : + } ); } diff --git a/frontend/src/features/api/apiSlice.js b/frontend/src/features/api/apiSlice.js index 8026c3f..c256cae 100644 --- a/frontend/src/features/api/apiSlice.js +++ b/frontend/src/features/api/apiSlice.js @@ -44,6 +44,10 @@ export const apiSlice = createApi({ // The URL for the request is '/fakeApi/posts' query: (shortName) => ({ url: `/${shortName}/talkgroups` }) }), + getStats: builder.query({ + // The URL for the request is '/fakeApi/posts' + query: (shortName) => ({ url: `/${shortName}/stats` }) + }), addNewEvent: builder.mutation({ query: initialEvent => ({ url: '/events', @@ -56,4 +60,4 @@ export const apiSlice = createApi({ }) // Export the auto-generated hook for the `getPosts` query endpoint -export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetEventsQuery, useGetEventQuery, useAddNewEventMutation } = apiSlice +export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetStatsQuery, useGetEventsQuery, useGetEventQuery, useAddNewEventMutation } = apiSlice diff --git a/frontend/src/features/callPlayer/callPlayerSlice.js b/frontend/src/features/callPlayer/callPlayerSlice.js index 7f6a475..245a38f 100644 --- a/frontend/src/features/callPlayer/callPlayerSlice.js +++ b/frontend/src/features/callPlayer/callPlayerSlice.js @@ -1,5 +1,5 @@ import { createSlice } from '@reduxjs/toolkit' -const now = new Date(); +const now = new Date().getTime(); const initialState = { isWaiting: false, shortName: "", diff --git a/frontend/src/features/calls/callsSlice.js b/frontend/src/features/calls/callsSlice.js index 8fa14c4..22da1d5 100644 --- a/frontend/src/features/calls/callsSlice.js +++ b/frontend/src/features/calls/callsSlice.js @@ -54,7 +54,7 @@ export const { selectAll: selectAllCalls, selectById: selectCallById } = callsAd return selectCallsData(state) ?? initialState }) */ -const now = new Date(); +const now = new Date().getTime(); export const callsAdapter = createEntityAdapter({ selectId: (call) => call._id, @@ -177,7 +177,7 @@ export const getCalls = createAsyncThunk( 'calls/getOlderCalls', async({},{getState, requestId} ) => { const {loading,oldestCallTime, newestCallTime} = getState().calls; - const url = buildCallUrl(getState,"older",oldestCallTime.getTime()); + const url = buildCallUrl(getState,"older",oldestCallTime); const res = await fetch(url).then( (data) => data.json() ) @@ -188,7 +188,7 @@ export const getCalls = createAsyncThunk( 'calls/getNewerCalls', async({},{getState, requestId} ) => { const {loading,oldestCallTime, newestCallTime} = getState().calls; - const url = buildCallUrl(getState,"newer",newestCallTime.getTime()); + const url = buildCallUrl(getState,"newer",newestCallTime); const res = await fetch(url).then( (data) => data.json() ) @@ -223,8 +223,8 @@ export const callsSlice = createSlice({ const firstTime = state.data.entities[first].time; const last = state.data.ids[state.data.ids.length-1]; const lastTime = state.data.entities[last].time; - state.newestCallTime = new Date(firstTime) - state.oldestCallTime = new Date(lastTime) + state.newestCallTime = new Date(firstTime).getTime(); + state.oldestCallTime = new Date(lastTime).getTime(); } }, [getOlderCalls.pending]: (state) => { @@ -242,8 +242,8 @@ export const callsSlice = createSlice({ const firstTime = state.data.entities[first].time; const last = state.data.ids[state.data.ids.length-1]; const lastTime = state.data.entities[last].time; - state.newestCallTime = new Date(firstTime) - state.oldestCallTime = new Date(lastTime) + state.newestCallTime = new Date(firstTime).getTime(); + state.oldestCallTime = new Date(lastTime).getTime(); } }, [getNewerCalls.pending]: (state) => { @@ -261,8 +261,8 @@ export const callsSlice = createSlice({ const firstTime = state.data.entities[first].time; const last = state.data.ids[state.data.ids.length-1]; const lastTime = state.data.entities[last].time; - state.newestCallTime = new Date(firstTime) - state.oldestCallTime = new Date(lastTime) + state.newestCallTime = new Date(firstTime).getTime(); + state.oldestCallTime = new Date(lastTime).getTime(); } }, [addStar.fulfilled]: (state, {payload}) => { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 165a2e0..e5986fa 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2674,6 +2674,47 @@ dependencies: "@types/node" "*" +"@types/d3-color@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.0.tgz#6594da178ded6c7c3842f3cc0ac84b156f12f2d4" + integrity sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA== + +"@types/d3-interpolate@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz#e7d17fa4a5830ad56fe22ce3b4fac8541a9572dc" + integrity sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@^1", "@types/d3-path@^1.0.8": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" + integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ== + +"@types/d3-scale@^4.0.2": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.3.tgz#7a5780e934e52b6f63ad9c24b105e33dd58102b5" + integrity sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^1.3.1": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.8.tgz#c3c15ec7436b4ce24e38de517586850f1fea8e89" + integrity sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg== + dependencies: + "@types/d3-path" "^1" + +"@types/d3-time@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" + integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== + +"@types/d3-time@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.1.1.tgz#743fdc821c81f86537cbfece07093ac39b4bc342" + integrity sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg== + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -2788,6 +2829,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash@^4.14.172": + version "4.14.192" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.192.tgz#5790406361a2852d332d41635d927f1600811285" + integrity sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A== + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -3039,6 +3085,84 @@ "@typescript-eslint/types" "5.47.0" eslint-visitor-keys "^3.3.0" +"@visx/axis@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@visx/axis/-/axis-3.1.0.tgz#775b221e5abfdec25304c607eeae8cda1a8bade6" + integrity sha512-JDj/1VYx0JO0pHFtwoFtYcnqdoZFh/dpHImEl169S5nTslSFlIoNTXA/ekpBP6ELkEZ59gmF1X5k29x6MFBwCA== + dependencies: + "@types/react" "*" + "@visx/group" "3.0.0" + "@visx/point" "3.0.1" + "@visx/scale" "3.0.0" + "@visx/shape" "3.0.0" + "@visx/text" "3.0.0" + classnames "^2.3.1" + prop-types "^15.6.0" + +"@visx/curve@3.0.0", "@visx/curve@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@visx/curve/-/curve-3.0.0.tgz#c54568472e00a38483c58cf52e4a6ddb2887c2d4" + integrity sha512-kvHJDLBeczTQ87ZExSTfRxej06l6o6UiQ0NHf9+xpAin06y6Qk1ThOHHWJTGM6KGzwlu7jEauJGHwZs6nMhDvA== + dependencies: + "@types/d3-shape" "^1.3.1" + d3-shape "^1.0.6" + +"@visx/group@3.0.0", "@visx/group@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@visx/group/-/group-3.0.0.tgz#e7f9752599bcc7e141ff5317a2a9a502577ab8df" + integrity sha512-SFjXhTMcsaVAb1/TVL1KM5vn8gQTIVgSx0ATdDl4BJSFp2ym1lO8LY4jpV4SFweaHnWxVwrrfGLTn5QsYnvmjQ== + dependencies: + "@types/react" "*" + classnames "^2.3.1" + prop-types "^15.6.2" + +"@visx/point@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@visx/point/-/point-3.0.1.tgz#77587ddaabf6f3023f09f8a0ce33a2c27c9d64c8" + integrity sha512-S5WOBMgEP2xHcgs3A2BFB2vwzrk0tMmn3PGZAbQJ+lu4HlnalDP72klUnxLTH8xclNNvpUHtHM5eLIJXyHx6Pw== + +"@visx/scale@3.0.0", "@visx/scale@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@visx/scale/-/scale-3.0.0.tgz#727123f0c930d3346a4473e926831c45997e0312" + integrity sha512-WSf+wrxZEvu5TPGfGTafzzX1MbogbIxfD9ZKM9p7xfw65v23G0dNMy4bqVBUbOJigONoQkIZyqQ+gz5AJ/ioIg== + dependencies: + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-time" "^2.0.0" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-time "^2.1.1" + +"@visx/shape@3.0.0", "@visx/shape@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@visx/shape/-/shape-3.0.0.tgz#a1d4bd0e12cc94c164252f175997932a09c24652" + integrity sha512-t6lpP9bIA1vwChDwiOUWl92ro29XF/M8IVNWRA0pm4LGxGGTACvxG3Agfcdi3JprahUVqPpnRCwuR36PDanq3Q== + dependencies: + "@types/d3-path" "^1.0.8" + "@types/d3-shape" "^1.3.1" + "@types/lodash" "^4.14.172" + "@types/react" "*" + "@visx/curve" "3.0.0" + "@visx/group" "3.0.0" + "@visx/scale" "3.0.0" + classnames "^2.3.1" + d3-path "^1.0.5" + d3-shape "^1.2.0" + lodash "^4.17.21" + prop-types "^15.5.10" + +"@visx/text@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@visx/text/-/text-3.0.0.tgz#9099c3605027b9ab4c54bde97518a648136c3629" + integrity sha512-LW6v5T/gpd9RGw83/ScXncYc6IlcfzXTpaN8WbbxLRI65gdvSqrykwAMR0cbpQmzoVFuZXljqOf0QslHGnBg1w== + dependencies: + "@types/lodash" "^4.14.172" + "@types/react" "*" + classnames "^2.3.1" + lodash "^4.17.21" + prop-types "^15.7.2" + reduce-css-calc "^1.3.0" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -3630,6 +3754,11 @@ babel-preset-react-app@^10.0.1: babel-plugin-macros "^3.1.0" babel-plugin-transform-react-remove-prop-types "^0.4.24" +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -3913,7 +4042,7 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -classnames@^2.2.6: +classnames@^2.2.6, classnames@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== @@ -4344,6 +4473,81 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +d3-array@2: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +"d3-array@2 - 3", "d3-array@2.10.0 - 3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.3.tgz#39f1f4954e4a09ff69ac597c2d61906b04e84740" + integrity sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@1, d3-path@^1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^1.0.6, d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-time@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" + integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== + dependencies: + d3-array "2" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -5911,6 +6115,16 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -7072,6 +7286,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +math-expression-evaluator@^1.2.14: + version "1.4.0" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz#3d66031117fbb7b9715ea6c9c68c2cd2eebd37e2" + integrity sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw== + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8288,7 +8507,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8638,6 +8857,22 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reduce-css-calc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA== + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f" + integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ== + dependencies: + balanced-match "^1.0.0" + redux-thunk@2.4.2, redux-thunk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"