From a89bea4fdee4a4e3bba68942b5028959f23ec3e4 Mon Sep 17 00:00:00 2001 From: a-thansen <102360418+a-thansen@users.noreply.github.com> Date: Mon, 9 Oct 2023 18:11:33 +0200 Subject: [PATCH] Added Data Filter (v1) Things to be improved: *customize, add date filter headline, choose nicer colors. *check if it is correctly choosing the dates and etc. to filter (imo it looks like it is) *update max and min month limits (depending on oldest data available) *format in a nicer way on the navbar --- frontend/.prettierrc | 3 +- frontend/package-lock.json | 92 ++++++++++++++++++- frontend/package.json | 2 + .../Components/Conditions/ConditionsMap.tsx | 74 ++++++--------- frontend/src/Components/Map/MonthFilter.tsx | 53 +++++++++++ 5 files changed, 177 insertions(+), 47 deletions(-) create mode 100644 frontend/src/Components/Map/MonthFilter.tsx diff --git a/frontend/.prettierrc b/frontend/.prettierrc index a20502b7..4510dc5c 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -1,4 +1,5 @@ { "singleQuote": true, - "trailingComma": "all" + "trailingComma": "all", + "endOfLine": "auto" } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6007bd84..9fc624b3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,6 +16,7 @@ "leaflet": "^1.9.4", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-datepicker": "^4.19.0", "react-dom": "^18.2.0", "react-gradient-hook": "^1.5.3", "react-leaflet": "^4.2.1", @@ -37,6 +38,7 @@ "@types/leaflet": "^1.9.4", "@types/node": "^20.6.2", "@types/react": "^18.2.21", + "@types/react-datepicker": "^4.15.1", "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", "@types/react-slider": "^1.3.1", @@ -2982,7 +2984,6 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4147,6 +4148,18 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-datepicker": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.15.1.tgz", + "integrity": "sha512-6/LthK0pTDBKjjVJqA2ygY3jJsHH7uZXIk8WPQcGHUiFOQLOKIv3krOxFZ2mt3BB3guMB6jVTc6ansQAd0r7xQ==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.9.2", + "@types/react": "*", + "date-fns": "^2.0.1", + "react-popper": "^2.2.5" + } + }, "node_modules/@types/react-dom": { "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", @@ -6310,6 +6323,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -7613,6 +7631,21 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -16134,6 +16167,23 @@ "react": "*" } }, + "node_modules/react-datepicker": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.19.0.tgz", + "integrity": "sha512-p2IVcOdtMDGvaq27yBGQm43rWnPVJ3w6EMGOmNoG3Gh/Li9maDIfrAWJUeLXDXAxYbXkIsQRs5SwrHvKP65juA==", + "dependencies": { + "@popperjs/core": "^2.11.8", + "classnames": "^2.2.6", + "date-fns": "^2.30.0", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0", + "react-popper": "^2.3.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -16344,6 +16394,11 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, "node_modules/react-gradient-hook": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/react-gradient-hook/-/react-gradient-hook-1.5.3.tgz", @@ -16404,6 +16459,33 @@ "resolved": "https://registry.npmjs.org/react-leaflet-hotline/-/react-leaflet-hotline-1.4.12.tgz", "integrity": "sha512-Pb3yl01g5OizMvdlWhTvMrzvN5E+Y7YyeMsvpXX4SiIl5o8q6TqnQy9Z+3NeO6UlM0c0z3geBTbQgsdq3phLwA==" }, + "node_modules/react-onclickoutside": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", + "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, + "node_modules/react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -20286,6 +20368,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 7410767e..a86a7c21 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "leaflet": "^1.9.4", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-datepicker": "^4.19.0", "react-dom": "^18.2.0", "react-gradient-hook": "^1.5.3", "react-leaflet": "^4.2.1", @@ -58,6 +59,7 @@ "@types/leaflet": "^1.9.4", "@types/node": "^20.6.2", "@types/react": "^18.2.21", + "@types/react-datepicker": "^4.15.1", "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", "@types/react-slider": "^1.3.1", diff --git a/frontend/src/Components/Conditions/ConditionsMap.tsx b/frontend/src/Components/Conditions/ConditionsMap.tsx index d0e3709d..5f428191 100644 --- a/frontend/src/Components/Conditions/ConditionsMap.tsx +++ b/frontend/src/Components/Conditions/ConditionsMap.tsx @@ -5,11 +5,13 @@ import { Layer, PathOptions } from 'leaflet'; import { Feature, FeatureCollection } from 'geojson'; import Search from '../Map/Search'; +import MonthFilter from '../Map/MonthFilter'; import MapWrapper from '../Map/MapWrapper'; import '../../css/navbar.css'; import '../../css/search.css'; import '../../css/slider.css'; +import 'react-datepicker/dist/react-datepicker.css'; import { getConditions } from '../../queries/fetchConditions'; @@ -53,44 +55,6 @@ const lessOrEqualThan = ( } }; -const noMonth = (dateRange: DateRange): number => { - if (dateRange.start === undefined || dateRange.end === undefined) { - return NaN; - } else { - if (!lessOrEqualThan(dateRange.start, dateRange.end)) { - return NaN; - } else { - return ( - (dateRange.end.year - dateRange.start.year) * 12 + - dateRange.end.month - - dateRange.start.month - ); - } - } -}; - -const noToYearMonth = ( - no: number, - dateRange: DateRange, -): YearMonth | undefined => { - if (dateRange.start !== undefined) { - const month = dateRange.start.month + no; - const normalizedMonth = ((month - 1) % 12) + 1; - const years = Math.floor((month - 1) / 12); - return { year: dateRange.start.year + years, month: normalizedMonth }; - } - return undefined; -}; - -const yearMonthtoText = (yearMonth: YearMonth | undefined): string => { - if (yearMonth === undefined) { - return '??/??'; - } - return ( - yearMonth.month.toString() + '/' + yearMonth.year.toString().substring(2) - ); -}; - const getTypeColor = (type: string): string => { switch (type) { case KPI: @@ -202,18 +166,28 @@ const ConditionsMap = (props: any) => { const [mode, setMode] = useState('ALL'); + const newSelectedRange: DateRange = {}; + const inputChange = ({ target }: any) => { setMode(target.value); }; - const rangeChange = (values: number[]) => { - if (values.length === 2) { - const newSelectedRange: DateRange = { - start: noToYearMonth(values[0], rangeAll), - end: noToYearMonth(values[1], rangeAll), - }; - setRangeSelected(newSelectedRange); + function dateChange(date: any) { + const YearMonth: YearMonth = { + year: date.getFullYear(), + month: date.getMonth() + 1, // +1 why + }; + + return YearMonth; + } + + const rangeChange = (d: YearMonth, start: boolean) => { + if (start) { + newSelectedRange.start = d; + } else { + newSelectedRange.end = d; } + setRangeSelected(newSelectedRange); }; useEffect(() => { @@ -425,6 +399,16 @@ const ConditionsMap = (props: any) => { ))} +
+ { + rangeChange(dateChange(date), true); + }} + onEndChange={(date: any) => { + rangeChange(dateChange(date), false); + }} + /> +
diff --git a/frontend/src/Components/Map/MonthFilter.tsx b/frontend/src/Components/Map/MonthFilter.tsx new file mode 100644 index 00000000..511c266a --- /dev/null +++ b/frontend/src/Components/Map/MonthFilter.tsx @@ -0,0 +1,53 @@ +import React, { useState } from 'react'; +import DatePicker from 'react-datepicker'; + +interface Props { + /** + * date: date input + * onStartChange: function taking argument "date" for Start Date + * onEndChange: function taking argument "date" for End date + */ + + onStartChange: (date: any) => void; + onEndChange: (date: any) => void; +} + +/** + * Component rendering Month Filter + */ + +const MonthFilter: React.FC = ({ onEndChange, onStartChange }) => { + const [startDate, setStartDate] = useState(new Date('2022/06/08')); + const [endDate, setEndDate] = useState(new Date()); + + return ( + <> + setStartDate(date)} + onChange={onStartChange} + selectsStart + startDate={startDate} + endDate={endDate} + minDate={new Date('2022/06/08')} + maxDate={new Date()} + dateFormat="MM/yyyy" + showMonthYearPicker + /> + setEndDate(date)} + onChange={onEndChange} + selectsEnd + startDate={startDate} + endDate={endDate} + minDate={new Date('2022/06/08')} + maxDate={new Date()} + dateFormat="MM/yyyy" + showMonthYearPicker + /> + + ); +}; + +export default MonthFilter;