onHover(true)}
onMouseLeave={() => onHover(false)}
>
diff --git a/web-new/src/pages/History.tsx b/web-new/src/pages/History.tsx
index a5ea4ea95..845441cb1 100644
--- a/web-new/src/pages/History.tsx
+++ b/web-new/src/pages/History.tsx
@@ -1,10 +1,138 @@
+import { useMemo, useState } from "react";
+import useSWR from "swr";
+import { FrigateConfig } from "@/types/frigateConfig";
import Heading from "@/components/ui/heading";
+import ActivityIndicator from "@/components/ui/activity-indicator";
+import HistoryCard from "@/components/card/HistoryCard";
+import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
+import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";;
function History() {
+ const { data: config } = useSWR
("config");
+ const timezone = useMemo(() => config?.ui?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone, [config]);
+ const { data: hourlyTimeline } = useSWR(['timeline/hourly', { timezone }]);
+ const { data: allPreviews } = useSWR(`preview/all/start/${hourlyTimeline?.start || 0}/end/${hourlyTimeline?.end || 0}`, { revalidateOnFocus: false });
+
+ const [detailLevel, setDetailLevel] = useState<'normal' | 'extra' | 'full'>('normal');
+
+ const timelineCards: CardsData | never[] = useMemo(() => {
+ if (!hourlyTimeline) {
+ return [];
+ }
+
+ const cards: CardsData = {};
+ Object.keys(hourlyTimeline["hours"])
+ .reverse()
+ .forEach((hour) => {
+ const day = new Date(parseInt(hour) * 1000);
+ day.setHours(0, 0, 0, 0);
+ const dayKey = (day.getTime() / 1000).toString();
+ const source_to_types: {[key: string]: string[]} = {};
+ Object.values(hourlyTimeline["hours"][hour]).forEach((i) => {
+ const time = new Date(i.timestamp * 1000);
+ time.setSeconds(0);
+ time.setMilliseconds(0);
+ const key = `${i.source_id}-${time.getMinutes()}`;
+ if (key in source_to_types) {
+ source_to_types[key].push(i.class_type);
+ } else {
+ source_to_types[key] = [i.class_type];
+ }
+ });
+
+ if (!Object.keys(cards).includes(dayKey)) {
+ cards[dayKey] = {};
+ }
+ cards[dayKey][hour] = {};
+ Object.values(hourlyTimeline["hours"][hour]).forEach((i) => {
+ const time = new Date(i.timestamp * 1000);
+ time.setSeconds(0);
+ time.setMilliseconds(0);
+ const key = `${i.camera}-${time.getMinutes()}`;
+
+ // detail level for saving items
+ // detail level determines which timeline items for each moment is returned
+ // values can be normal, extra, or full
+ // normal: return all items except active / attribute / gone / stationary / visible unless that is the only item.
+ // extra: return all items except attribute / gone / visible unless that is the only item
+ // full: return all items
+
+ let add = true;
+ if (detailLevel == 'normal') {
+ if (
+ source_to_types[`${i.source_id}-${time.getMinutes()}`].length > 1 &&
+ ['active', 'attribute', 'gone', 'stationary', 'visible'].includes(i.class_type)
+ ) {
+ add = false;
+ }
+ } else if (detailLevel == 'extra') {
+ if (
+ source_to_types[`${i.source_id}-${time.getMinutes()}`].length > 1 &&
+ i.class_type in ['attribute', 'gone', 'visible']
+ ) {
+ add = false;
+ }
+ }
+
+ if (add) {
+ if (key in cards[dayKey][hour]) {
+ cards[dayKey][hour][key].entries.push(i);
+ } else {
+ cards[dayKey][hour][key] = {
+ camera: i.camera,
+ time: time.getTime() / 1000,
+ entries: [i],
+ };
+ }
+ }
+ });
+ });
+
+ return cards;
+ }, [detailLevel, hourlyTimeline]);
+
+ if (!config || !timelineCards) {
+ return ;
+ }
+
return (
- <>
- History
- >
+ <>
+ Review
+ Dates and times are based on the timezone {timezone}
+
+
+ {Object.entries(timelineCards).reverse().map(([day, timelineDay]) => {
+ return (
+
+
+ {formatUnixTimestampToDateTime(parseInt(day), { strftime_fmt: '%A %b %d' })}
+
+ {Object.entries(timelineDay).map(([hour, timelineHour]) => {
+ if (Object.values(timelineHour).length == 0) {
+ return <>>;
+ }
+
+ return (
+
+
+ {formatUnixTimestampToDateTime(parseInt(hour), { strftime_fmt: '%I:00' })}
+
+
+
+ {Object.entries(timelineHour).map(([key, timeline]) => {
+ return
+ })}
+
+
+
+
+ );
+ })}
+
+ );
+ })}
+
+ >
);
}
diff --git a/web-new/src/pages/Review.tsx b/web-new/src/pages/Review.tsx
deleted file mode 100644
index 0f33b0b40..000000000
--- a/web-new/src/pages/Review.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { useMemo, useState } from "react";
-import useSWR from "swr";
-import { FrigateConfig } from "@/types/frigateConfig";
-import Heading from "@/components/ui/heading";
-import ActivityIndicator from "@/components/ui/activity-indicator";
-import ReviewCard from "@/components/card/ReviewCard";
-import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
-import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
-
-export function Review() {
- const { data: config } = useSWR("config");
- const timezone = useMemo(() => config?.ui?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone, [config]);
- const { data: hourlyTimeline } = useSWR(['timeline/hourly', { timezone }]);
- const { data: allPreviews } = useSWR(`preview/all/start/${hourlyTimeline?.start || 0}/end/${hourlyTimeline?.end || 0}`, { revalidateOnFocus: false });
-
- const [detailLevel, setDetailLevel] = useState<'normal' | 'extra' | 'full'>('normal');
-
- const timelineCards: CardsData | never[] = useMemo(() => {
- if (!hourlyTimeline) {
- return [];
- }
-
- const cards: CardsData = {};
- Object.keys(hourlyTimeline["hours"])
- .reverse()
- .forEach((hour) => {
- const day = new Date(parseInt(hour) * 1000);
- day.setHours(0, 0, 0, 0);
- const dayKey = (day.getTime() / 1000).toString();
- const source_to_types: {[key: string]: string[]} = {};
- Object.values(hourlyTimeline["hours"][hour]).forEach((i) => {
- const time = new Date(i.timestamp * 1000);
- time.setSeconds(0);
- time.setMilliseconds(0);
- const key = `${i.source_id}-${time.getMinutes()}`;
- if (key in source_to_types) {
- source_to_types[key].push(i.class_type);
- } else {
- source_to_types[key] = [i.class_type];
- }
- });
-
- if (!Object.keys(cards).includes(dayKey)) {
- cards[dayKey] = {};
- }
- cards[dayKey][hour] = {};
- Object.values(hourlyTimeline["hours"][hour]).forEach((i) => {
- const time = new Date(i.timestamp * 1000);
- time.setSeconds(0);
- time.setMilliseconds(0);
- const key = `${i.camera}-${time.getMinutes()}`;
-
- // detail level for saving items
- // detail level determines which timeline items for each moment is returned
- // values can be normal, extra, or full
- // normal: return all items except active / attribute / gone / stationary / visible unless that is the only item.
- // extra: return all items except attribute / gone / visible unless that is the only item
- // full: return all items
-
- let add = true;
- if (detailLevel == 'normal') {
- if (
- source_to_types[`${i.source_id}-${time.getMinutes()}`].length > 1 &&
- ['active', 'attribute', 'gone', 'stationary', 'visible'].includes(i.class_type)
- ) {
- add = false;
- }
- } else if (detailLevel == 'extra') {
- if (
- source_to_types[`${i.source_id}-${time.getMinutes()}`].length > 1 &&
- i.class_type in ['attribute', 'gone', 'visible']
- ) {
- add = false;
- }
- }
-
- if (add) {
- if (key in cards[dayKey][hour]) {
- cards[dayKey][hour][key].entries.push(i);
- } else {
- cards[dayKey][hour][key] = {
- camera: i.camera,
- time: time.getTime() / 1000,
- entries: [i],
- };
- }
- }
- });
- });
-
- return cards;
- }, [detailLevel, hourlyTimeline]);
-
- if (!config || !timelineCards) {
- return ;
- }
-
- return (
- <>
- Review
- Dates and times are based on the timezone {timezone}
-
-
- {Object.entries(timelineCards).reverse().map(([day, timelineDay]) => {
- return (
-
-
- {formatUnixTimestampToDateTime(parseInt(day), { strftime_fmt: '%A %b %d' })}
-
- {Object.entries(timelineDay).map(([hour, timelineHour]) => {
- if (Object.values(timelineHour).length == 0) {
- return <>>;
- }
-
- return (
-
-
- {formatUnixTimestampToDateTime(parseInt(hour), { strftime_fmt: '%I:00' })}
-
-
-
- {Object.entries(timelineHour).map(([key, timeline]) => {
- return
- })}
-
-
-
-
- );
- })}
-
- );
- })}
-
- >
- );
-}
-
-export default Review
\ No newline at end of file
diff --git a/web-new/src/types/review.ts b/web-new/src/types/history.ts
similarity index 100%
rename from web-new/src/types/review.ts
rename to web-new/src/types/history.ts