From d56c9c6d840151564896b29c1df80e1035265d69 Mon Sep 17 00:00:00 2001 From: Thomas Schoffelen Date: Sat, 7 Sep 2024 16:03:49 +0100 Subject: [PATCH] fix: make date picker work with specific times (fixes #15) --- packages/dashboard/package.json | 2 +- .../src/components/layout/date-picker.tsx | 193 ++++++++++-------- .../dashboard/src/components/ui/button.tsx | 2 +- .../dashboard/src/components/ui/calendar.tsx | 64 ++++++ yarn.lock | 46 +---- 5 files changed, 181 insertions(+), 126 deletions(-) create mode 100644 packages/dashboard/src/components/ui/calendar.tsx diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index 95812dd..85803c8 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -27,7 +27,7 @@ "lucide-react": "^0.437.0", "react": "^18.3.1", "react-cytoscapejs": "^2.0.0", - "react-date-range": "^2.0.1", + "react-day-picker": "8.10.1", "react-dom": "^18.3.1", "react-json-tree": "^0.19.0", "react-router-dom": "^6.26.1", diff --git a/packages/dashboard/src/components/layout/date-picker.tsx b/packages/dashboard/src/components/layout/date-picker.tsx index c77f47b..6dcb2f9 100644 --- a/packages/dashboard/src/components/layout/date-picker.tsx +++ b/packages/dashboard/src/components/layout/date-picker.tsx @@ -1,26 +1,51 @@ -import { createContext, useContext, useState } from "react"; -import { DateRangePicker, createStaticRanges } from "react-date-range"; -import { Calendar } from "lucide-react"; -import { subDays, format, subHours } from "date-fns"; -import { enGB } from "date-fns/locale"; +import { createContext, useContext, useEffect, useState } from "react"; +import { CalendarIcon } from "lucide-react"; +import { subDays, format, subHours, parse } from "date-fns"; -import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; - -import "react-date-range/dist/styles.css"; -import "react-date-range/dist/theme/default.css"; +import { Button } from "@/components/ui/button"; +import { Calendar } from "@/components/ui/calendar"; +import { Input } from "@/components/ui/input"; const store = createContext({}); export const useDateRange = () => useContext(store); const { Provider } = store; +const ranges = [ + { + label: "Last hours", + startDate: subHours(new Date(), 1), + endDate: new Date(), + }, + { + label: "Last 4 hours", + startDate: subHours(new Date(), 4), + endDate: new Date(), + }, + { + label: "Last 24 hours", + startDate: subDays(new Date(), 1), + endDate: new Date(), + }, + { + label: "Last 3 days", + startDate: subDays(new Date(), 3), + endDate: new Date(), + }, + { + label: "Last 7 days", + startDate: subDays(new Date(), 7), + endDate: new Date(), + }, +]; + export const DateRangeProvider = ({ children }) => { - const [startDate, setStartDate] = useState(subDays(new Date(), 1)); - const [endDate, setEndDate] = useState(new Date()); + const [startDate, setStartDate] = useState(ranges[2].startDate); + const [endDate, setEndDate] = useState(ranges[2].endDate); const [label, setLabel] = useState("Last 24 hours"); return ( @@ -43,76 +68,26 @@ const DatePicker = ({}) => { const { startDate, endDate, label, setStartDate, setEndDate, setLabel } = useDateRange(); const [open, setOpen] = useState(false); - const [dateRange, setDateRange] = useState([ - { startDate, endDate, key: "selection" }, - ]); - const handleClose = (range) => { - setOpen(false); - const { startDate, endDate } = range[0]; - const selected = ranges.find( - (option) => option.isSelected(range[0]) === true, + useEffect(() => { + const range = ranges.find( + (range) => + range.startDate.valueOf() === startDate.valueOf() && + range.endDate.valueOf() === endDate.valueOf(), ); - const label = selected - ? selected.label - : `${format(startDate, "PP")} – ${format(endDate, "PP")}`; - - setStartDate(startDate); - setEndDate(endDate); - setLabel(label); - }; + setLabel( + range?.label || `${format(startDate, "PP")} – ${format(endDate, "PP")}`, + ); + }, [startDate, endDate]); - const getDayIndex = (date) => Math.floor(date.valueOf() / 86400000); + const handleClose = ({ to, from, label = "" }) => { + setOpen(false); - const handleSelect = (item) => { - setDateRange([item.selection]); - if ( - getDayIndex(item.selection.startDate) !== - getDayIndex(item.selection.endDate) - ) { - // automatically close after a new range has been selected - handleClose([item.selection]); - } + setStartDate(to); + setEndDate(from); + setLabel(label || `${format(to, "PP")} – ${format(from, "PP")}`); }; - const ranges = createStaticRanges([ - { - label: "Last hours", - range: () => ({ - startDate: subHours(new Date(), 1), - endDate: new Date(), - }), - }, - { - label: "Last 4 hours", - range: () => ({ - startDate: subHours(new Date(), 4), - endDate: new Date(), - }), - }, - { - label: "Last 24 hours", - range: () => ({ - startDate: subDays(new Date(), 1), - endDate: new Date(), - }), - }, - { - label: "Last 3 days", - range: () => ({ - startDate: subDays(new Date(), 3), - endDate: new Date(), - }), - }, - { - label: "Last 7 days", - range: () => ({ - startDate: subDays(new Date(), 7), - endDate: new Date(), - }), - }, - ]); - return ( setOpen(open)}> @@ -121,21 +96,67 @@ const DatePicker = ({}) => { onClick={() => setOpen(true)} className="px-3" > - + {label} - +
+
+ {ranges.map((option) => ( + + ))} +
+
+ { + if (range?.from) { + setStartDate(range.from); + } + if (range?.to) { + setEndDate(range.to); + } + }} + /> +
+ + setStartDate(parse(e.target.value, "HH:mm", startDate)) + } + /> + + + setEndDate(parse(e.target.value, "HH:mm", endDate)) + } + /> +
+
+
); diff --git a/packages/dashboard/src/components/ui/button.tsx b/packages/dashboard/src/components/ui/button.tsx index 0d19311..9025653 100644 --- a/packages/dashboard/src/components/ui/button.tsx +++ b/packages/dashboard/src/components/ui/button.tsx @@ -10,7 +10,7 @@ const buttonVariants = cva( variants: { variant: { default: - "bg-primary text-primary-foreground shadow hover:bg-primary/90", + "bg-primary text-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", outline: diff --git a/packages/dashboard/src/components/ui/calendar.tsx b/packages/dashboard/src/components/ui/calendar.tsx new file mode 100644 index 0000000..e19a402 --- /dev/null +++ b/packages/dashboard/src/components/ui/calendar.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import { ChevronLeft, ChevronRight } from "lucide-react" +import { DayPicker } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + , + IconRight: ({ ...props }) => , + }} + {...props} + /> + ) +} +Calendar.displayName = "Calendar" + +export { Calendar } diff --git a/yarn.lock b/yarn.lock index b5b61d2..67e3322 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5994,7 +5994,7 @@ __metadata: postcss: "npm:^8.4.41" react: "npm:^18.3.1" react-cytoscapejs: "npm:^2.0.0" - react-date-range: "npm:^2.0.1" + react-day-picker: "npm:8.10.1" react-dom: "npm:^18.3.1" react-json-tree: "npm:^0.19.0" react-router-dom: "npm:^6.26.1" @@ -7707,13 +7707,6 @@ __metadata: languageName: node linkType: hard -"classnames@npm:^2.2.6": - version: 2.5.1 - resolution: "classnames@npm:2.5.1" - checksum: 10c0/afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 - languageName: node - linkType: hard - "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -15062,7 +15055,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:15, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -15232,18 +15225,13 @@ __metadata: languageName: node linkType: hard -"react-date-range@npm:^2.0.1": - version: 2.0.1 - resolution: "react-date-range@npm:2.0.1" - dependencies: - classnames: "npm:^2.2.6" - prop-types: "npm:^15.7.2" - react-list: "npm:^0.8.13" - shallow-equal: "npm:^1.2.1" +"react-day-picker@npm:8.10.1": + version: 8.10.1 + resolution: "react-day-picker@npm:8.10.1" peerDependencies: - date-fns: 3.0.6 || >=3.0.0 - react: ^0.14 || ^15.0.0-rc || >=15.0 - checksum: 10c0/be419f377b86cd9eafbe1e4fb60ba20b81b173e64dbe6cab8649edbe2a9afbc97f23701ab1e382caf4d82f65aaf4746e89d3df8cbbeb127df9ea4b7a3d11a8ab + date-fns: ^2.28.0 || ^3.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/a0ff28c4b61b3882e6a825b19e5679e2fdf3256cf1be8eb0a0c028949815c1ae5a6561474c2c19d231c010c8e0e0b654d3a322610881e0655abca05a2e03d9df languageName: node linkType: hard @@ -15286,17 +15274,6 @@ __metadata: languageName: node linkType: hard -"react-list@npm:^0.8.13": - version: 0.8.17 - resolution: "react-list@npm:0.8.17" - dependencies: - prop-types: "npm:15" - peerDependencies: - react: 0.14 || 15 - 18 - checksum: 10c0/85b2324b9fb410fcb3c6811578d8de51ec84eb308c320a865664c7f161fa3f70510fcc15ec7786e2725cac981355a937683b37d91a66ccfdffacf92c9e40326c - languageName: node - linkType: hard - "react-refresh@npm:^0.14.2": version: 0.14.2 resolution: "react-refresh@npm:0.14.2" @@ -16382,13 +16359,6 @@ __metadata: languageName: node linkType: hard -"shallow-equal@npm:^1.2.1": - version: 1.2.1 - resolution: "shallow-equal@npm:1.2.1" - checksum: 10c0/51e03abadd97c9ebe590547d92db9148446962a3f23a3a0fb1ba2fccab80af881eef0ff1f8ccefd3f066c0bc5a4c8ca53706194813b95c8835fa66448a843a26 - languageName: node - linkType: hard - "shebang-command@npm:^1.2.0": version: 1.2.0 resolution: "shebang-command@npm:1.2.0"