diff --git a/web/src/components/filter/ReviewFilterGroup.tsx b/web/src/components/filter/ReviewFilterGroup.tsx index 0ebb00a20..12434f67e 100644 --- a/web/src/components/filter/ReviewFilterGroup.tsx +++ b/web/src/components/filter/ReviewFilterGroup.tsx @@ -10,8 +10,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu"; -import { Calendar } from "../ui/calendar"; -import { ReviewFilter } from "@/types/review"; +import { ReviewFilter, ReviewSummary } from "@/types/review"; import { getEndOfDayTimestamp } from "@/utils/dateUtil"; import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { FaCalendarAlt, FaFilter, FaVideo } from "react-icons/fa"; @@ -20,15 +19,18 @@ import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import { Switch } from "../ui/switch"; import { Label } from "../ui/label"; import FilterCheckBox from "./FilterCheckBox"; +import ReviewActivityCalendar from "../overlay/ReviewActivityCalendar"; const ATTRIBUTES = ["amazon", "face", "fedex", "license_plate", "ups"]; type ReviewFilterGroupProps = { + reviewSummary?: ReviewSummary; filter?: ReviewFilter; onUpdateFilter: (filter: ReviewFilter) => void; }; export default function ReviewFilterGroup({ + reviewSummary, filter, onUpdateFilter, }: ReviewFilterGroupProps) { @@ -102,6 +104,7 @@ export default function ReviewFilterGroup({ }} /> void; }; function CalendarFilterButton({ + reviewSummary, day, updateSelectedDay, }: CalendarFilterButtonProps) { - const disabledDates = useMemo(() => { - const tomorrow = new Date(); - tomorrow.setHours(tomorrow.getHours() + 24, -1, 0, 0); - const future = new Date(); - future.setFullYear(tomorrow.getFullYear() + 10); - return { from: tomorrow, to: future }; - }, []); const selectedDate = useFormattedTimestamp( - day == undefined ? 0 : day?.getTime() / 1000, + day == undefined ? 0 : day?.getTime() / 1000 + 1, "%b %-d", ); @@ -302,14 +300,10 @@ function CalendarFilterButton({ ); const content = ( <> - { - updateSelectedDay(day); - }} +
diff --git a/web/src/components/overlay/ReviewActivityCalendar.tsx b/web/src/components/overlay/ReviewActivityCalendar.tsx new file mode 100644 index 000000000..009339fc8 --- /dev/null +++ b/web/src/components/overlay/ReviewActivityCalendar.tsx @@ -0,0 +1,78 @@ +import { ReviewSummary } from "@/types/review"; +import { Calendar } from "../ui/calendar"; +import { useMemo } from "react"; +import { FaCircle } from "react-icons/fa"; + +type ReviewActivityCalendarProps = { + reviewSummary?: ReviewSummary; + selectedDay?: Date; + onSelect: (day?: Date) => void; +}; +export default function ReviewActivityCalendar({ + reviewSummary, + selectedDay, + onSelect, +}: ReviewActivityCalendarProps) { + const disabledDates = useMemo(() => { + const tomorrow = new Date(); + tomorrow.setHours(tomorrow.getHours() + 24, -1, 0, 0); + const future = new Date(); + future.setFullYear(tomorrow.getFullYear() + 10); + return { from: tomorrow, to: future }; + }, []); + + return ( + ( + + ), + }} + /> + ); +} + +type ReviewActivityDayProps = { + reviewSummary?: ReviewSummary; + day: Date; +}; +function ReviewActivityDay({ reviewSummary, day }: ReviewActivityDayProps) { + const dayActivity = useMemo(() => { + if (!reviewSummary) { + return "none"; + } + + const allActivity = + reviewSummary[ + `${day.getFullYear()}-${("0" + (day.getMonth() + 1)).slice(-2)}-${("0" + day.getDate()).slice(-2)}` + ]; + + if (!allActivity) { + return "none"; + } + + if (allActivity.total_alert > allActivity.reviewed_alert) { + return "alert"; + } else if (allActivity.total_detection > allActivity.reviewed_detection) { + return "detection"; + } else { + return "none"; + } + }, [reviewSummary, day]); + + return ( +
+ {day.getDate()} + {dayActivity != "none" && ( + + )} +
+ ); +} diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index eaae92a0f..b78a45703 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -238,7 +238,11 @@ export default function EventView({ {selectedReviews.length <= 0 ? ( - + ) : (