mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Add severity filter (#11190)
* Allow viewing all types on single screen * Implement for mobile as well * fix import * Show all is optional
This commit is contained in:
parent
499f70cfd3
commit
b5b819c866
@ -3,24 +3,27 @@ import { Label } from "../ui/label";
|
|||||||
|
|
||||||
type FilterSwitchProps = {
|
type FilterSwitchProps = {
|
||||||
label: string;
|
label: string;
|
||||||
|
disabled?: boolean;
|
||||||
isChecked: boolean;
|
isChecked: boolean;
|
||||||
onCheckedChange: (checked: boolean) => void;
|
onCheckedChange: (checked: boolean) => void;
|
||||||
};
|
};
|
||||||
export default function FilterSwitch({
|
export default function FilterSwitch({
|
||||||
label,
|
label,
|
||||||
|
disabled = false,
|
||||||
isChecked,
|
isChecked,
|
||||||
onCheckedChange,
|
onCheckedChange,
|
||||||
}: FilterSwitchProps) {
|
}: FilterSwitchProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center gap-1">
|
<div className="flex justify-between items-center gap-1">
|
||||||
<Label
|
<Label
|
||||||
className="w-full mx-2 text-primary capitalize cursor-pointer"
|
className={`w-full mx-2 text-primary capitalize cursor-pointer ${disabled ? "text-secondary-foreground" : ""}`}
|
||||||
htmlFor={label}
|
htmlFor={label}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Label>
|
</Label>
|
||||||
<Switch
|
<Switch
|
||||||
id={label}
|
id={label}
|
||||||
|
disabled={disabled}
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
onCheckedChange={onCheckedChange}
|
onCheckedChange={onCheckedChange}
|
||||||
/>
|
/>
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "../ui/dropdown-menu";
|
} from "../ui/dropdown-menu";
|
||||||
import { ReviewFilter, ReviewSummary } from "@/types/review";
|
import { ReviewFilter, ReviewSeverity, ReviewSummary } from "@/types/review";
|
||||||
import { getEndOfDayTimestamp } from "@/utils/dateUtil";
|
import { getEndOfDayTimestamp } from "@/utils/dateUtil";
|
||||||
import { useFormattedTimestamp } from "@/hooks/use-date-utils";
|
import { useFormattedTimestamp } from "@/hooks/use-date-utils";
|
||||||
import {
|
import {
|
||||||
@ -49,19 +49,21 @@ const DEFAULT_REVIEW_FILTERS: ReviewFilters[] = [
|
|||||||
|
|
||||||
type ReviewFilterGroupProps = {
|
type ReviewFilterGroupProps = {
|
||||||
filters?: ReviewFilters[];
|
filters?: ReviewFilters[];
|
||||||
|
currentSeverity?: ReviewSeverity;
|
||||||
reviewSummary?: ReviewSummary;
|
reviewSummary?: ReviewSummary;
|
||||||
filter?: ReviewFilter;
|
filter?: ReviewFilter;
|
||||||
onUpdateFilter: (filter: ReviewFilter) => void;
|
|
||||||
motionOnly: boolean;
|
motionOnly: boolean;
|
||||||
|
onUpdateFilter: (filter: ReviewFilter) => void;
|
||||||
setMotionOnly: React.Dispatch<React.SetStateAction<boolean>>;
|
setMotionOnly: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ReviewFilterGroup({
|
export default function ReviewFilterGroup({
|
||||||
filters = DEFAULT_REVIEW_FILTERS,
|
filters = DEFAULT_REVIEW_FILTERS,
|
||||||
|
currentSeverity,
|
||||||
reviewSummary,
|
reviewSummary,
|
||||||
filter,
|
filter,
|
||||||
onUpdateFilter,
|
|
||||||
motionOnly,
|
motionOnly,
|
||||||
|
onUpdateFilter,
|
||||||
setMotionOnly,
|
setMotionOnly,
|
||||||
}: ReviewFilterGroupProps) {
|
}: ReviewFilterGroupProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
@ -179,6 +181,11 @@ export default function ReviewFilterGroup({
|
|||||||
<GeneralFilterButton
|
<GeneralFilterButton
|
||||||
allLabels={filterValues.labels}
|
allLabels={filterValues.labels}
|
||||||
selectedLabels={filter?.labels}
|
selectedLabels={filter?.labels}
|
||||||
|
currentSeverity={currentSeverity}
|
||||||
|
showAll={filter?.showAll == true}
|
||||||
|
setShowAll={(showAll) => {
|
||||||
|
onUpdateFilter({ ...filter, showAll });
|
||||||
|
}}
|
||||||
updateLabelFilter={(newLabels) => {
|
updateLabelFilter={(newLabels) => {
|
||||||
onUpdateFilter({ ...filter, labels: newLabels });
|
onUpdateFilter({ ...filter, labels: newLabels });
|
||||||
}}
|
}}
|
||||||
@ -188,6 +195,7 @@ export default function ReviewFilterGroup({
|
|||||||
<MobileReviewSettingsDrawer
|
<MobileReviewSettingsDrawer
|
||||||
features={mobileSettingsFeatures}
|
features={mobileSettingsFeatures}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
|
currentSeverity={currentSeverity}
|
||||||
reviewSummary={reviewSummary}
|
reviewSummary={reviewSummary}
|
||||||
onUpdateFilter={onUpdateFilter}
|
onUpdateFilter={onUpdateFilter}
|
||||||
// not applicable as exports are not used
|
// not applicable as exports are not used
|
||||||
@ -477,11 +485,17 @@ function CalendarFilterButton({
|
|||||||
type GeneralFilterButtonProps = {
|
type GeneralFilterButtonProps = {
|
||||||
allLabels: string[];
|
allLabels: string[];
|
||||||
selectedLabels: string[] | undefined;
|
selectedLabels: string[] | undefined;
|
||||||
|
currentSeverity?: ReviewSeverity;
|
||||||
|
showAll: boolean;
|
||||||
|
setShowAll: (showAll: boolean) => void;
|
||||||
updateLabelFilter: (labels: string[] | undefined) => void;
|
updateLabelFilter: (labels: string[] | undefined) => void;
|
||||||
};
|
};
|
||||||
function GeneralFilterButton({
|
function GeneralFilterButton({
|
||||||
allLabels,
|
allLabels,
|
||||||
selectedLabels,
|
selectedLabels,
|
||||||
|
currentSeverity,
|
||||||
|
showAll,
|
||||||
|
setShowAll,
|
||||||
updateLabelFilter,
|
updateLabelFilter,
|
||||||
}: GeneralFilterButtonProps) {
|
}: GeneralFilterButtonProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@ -510,6 +524,9 @@ function GeneralFilterButton({
|
|||||||
allLabels={allLabels}
|
allLabels={allLabels}
|
||||||
selectedLabels={selectedLabels}
|
selectedLabels={selectedLabels}
|
||||||
currentLabels={currentLabels}
|
currentLabels={currentLabels}
|
||||||
|
currentSeverity={currentSeverity}
|
||||||
|
showAll={showAll}
|
||||||
|
setShowAll={setShowAll}
|
||||||
updateLabelFilter={updateLabelFilter}
|
updateLabelFilter={updateLabelFilter}
|
||||||
setCurrentLabels={setCurrentLabels}
|
setCurrentLabels={setCurrentLabels}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
@ -557,6 +574,9 @@ type GeneralFilterContentProps = {
|
|||||||
allLabels: string[];
|
allLabels: string[];
|
||||||
selectedLabels: string[] | undefined;
|
selectedLabels: string[] | undefined;
|
||||||
currentLabels: string[] | undefined;
|
currentLabels: string[] | undefined;
|
||||||
|
currentSeverity?: ReviewSeverity;
|
||||||
|
showAll?: boolean;
|
||||||
|
setShowAll?: (showAll: boolean) => void;
|
||||||
updateLabelFilter: (labels: string[] | undefined) => void;
|
updateLabelFilter: (labels: string[] | undefined) => void;
|
||||||
setCurrentLabels: (labels: string[] | undefined) => void;
|
setCurrentLabels: (labels: string[] | undefined) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -565,6 +585,9 @@ export function GeneralFilterContent({
|
|||||||
allLabels,
|
allLabels,
|
||||||
selectedLabels,
|
selectedLabels,
|
||||||
currentLabels,
|
currentLabels,
|
||||||
|
currentSeverity,
|
||||||
|
showAll,
|
||||||
|
setShowAll,
|
||||||
updateLabelFilter,
|
updateLabelFilter,
|
||||||
setCurrentLabels,
|
setCurrentLabels,
|
||||||
onClose,
|
onClose,
|
||||||
@ -572,6 +595,25 @@ export function GeneralFilterContent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden">
|
<div className="h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden">
|
||||||
|
{currentSeverity && setShowAll && (
|
||||||
|
<div className="my-2.5 flex flex-col gap-2.5">
|
||||||
|
<FilterSwitch
|
||||||
|
label="Alerts"
|
||||||
|
disabled={currentSeverity == "alert"}
|
||||||
|
isChecked={currentSeverity == "alert" ? true : showAll == true}
|
||||||
|
onCheckedChange={setShowAll}
|
||||||
|
/>
|
||||||
|
<FilterSwitch
|
||||||
|
label="Detections"
|
||||||
|
disabled={currentSeverity == "detection"}
|
||||||
|
isChecked={
|
||||||
|
currentSeverity == "detection" ? true : showAll == true
|
||||||
|
}
|
||||||
|
onCheckedChange={setShowAll}
|
||||||
|
/>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex justify-between items-center my-2.5">
|
<div className="flex justify-between items-center my-2.5">
|
||||||
<Label
|
<Label
|
||||||
className="mx-2 text-primary cursor-pointer"
|
className="mx-2 text-primary cursor-pointer"
|
||||||
|
@ -7,7 +7,7 @@ import { ExportContent } from "./ExportDialog";
|
|||||||
import { ExportMode } from "@/types/filter";
|
import { ExportMode } from "@/types/filter";
|
||||||
import ReviewActivityCalendar from "./ReviewActivityCalendar";
|
import ReviewActivityCalendar from "./ReviewActivityCalendar";
|
||||||
import { SelectSeparator } from "../ui/select";
|
import { SelectSeparator } from "../ui/select";
|
||||||
import { ReviewFilter, ReviewSummary } from "@/types/review";
|
import { ReviewFilter, ReviewSeverity, ReviewSummary } from "@/types/review";
|
||||||
import { getEndOfDayTimestamp } from "@/utils/dateUtil";
|
import { getEndOfDayTimestamp } from "@/utils/dateUtil";
|
||||||
import { GeneralFilterContent } from "../filter/ReviewFilterGroup";
|
import { GeneralFilterContent } from "../filter/ReviewFilterGroup";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -31,6 +31,7 @@ type MobileReviewSettingsDrawerProps = {
|
|||||||
features?: DrawerFeatures[];
|
features?: DrawerFeatures[];
|
||||||
camera: string;
|
camera: string;
|
||||||
filter?: ReviewFilter;
|
filter?: ReviewFilter;
|
||||||
|
currentSeverity?: ReviewSeverity;
|
||||||
latestTime: number;
|
latestTime: number;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
range?: TimeRange;
|
range?: TimeRange;
|
||||||
@ -44,6 +45,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
features = DEFAULT_DRAWER_FEATURES,
|
features = DEFAULT_DRAWER_FEATURES,
|
||||||
camera,
|
camera,
|
||||||
filter,
|
filter,
|
||||||
|
currentSeverity,
|
||||||
latestTime,
|
latestTime,
|
||||||
currentTime,
|
currentTime,
|
||||||
range,
|
range,
|
||||||
@ -263,6 +265,11 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
allLabels={allLabels}
|
allLabels={allLabels}
|
||||||
selectedLabels={filter?.labels}
|
selectedLabels={filter?.labels}
|
||||||
currentLabels={currentLabels}
|
currentLabels={currentLabels}
|
||||||
|
currentSeverity={currentSeverity}
|
||||||
|
showAll={filter?.showAll == true}
|
||||||
|
setShowAll={(showAll) => {
|
||||||
|
onUpdateFilter({ ...filter, showAll });
|
||||||
|
}}
|
||||||
setCurrentLabels={setCurrentLabels}
|
setCurrentLabels={setCurrentLabels}
|
||||||
updateLabelFilter={(newLabels) =>
|
updateLabelFilter={(newLabels) =>
|
||||||
onUpdateFilter({ ...filter, labels: newLabels })
|
onUpdateFilter({ ...filter, labels: newLabels })
|
||||||
|
@ -26,6 +26,7 @@ export type ReviewFilter = {
|
|||||||
before?: number;
|
before?: number;
|
||||||
after?: number;
|
after?: number;
|
||||||
showReviewed?: 0 | 1;
|
showReviewed?: 0 | 1;
|
||||||
|
showAll?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ReviewSummaryDay = {
|
type ReviewSummaryDay = {
|
||||||
|
@ -270,6 +270,7 @@ export default function EventView({
|
|||||||
? ["cameras", "date", "motionOnly"]
|
? ["cameras", "date", "motionOnly"]
|
||||||
: ["cameras", "reviewed", "date", "general"]
|
: ["cameras", "reviewed", "date", "general"]
|
||||||
}
|
}
|
||||||
|
currentSeverity={severityToggle}
|
||||||
reviewSummary={reviewSummary}
|
reviewSummary={reviewSummary}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onUpdateFilter={updateFilter}
|
onUpdateFilter={updateFilter}
|
||||||
@ -370,7 +371,13 @@ function DetectionReview({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const current = reviewItems[severity];
|
let current;
|
||||||
|
|
||||||
|
if (filter?.showAll) {
|
||||||
|
current = reviewItems.all;
|
||||||
|
} else {
|
||||||
|
current = reviewItems[severity];
|
||||||
|
}
|
||||||
|
|
||||||
if (!current || current.length == 0) {
|
if (!current || current.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
|
Loading…
Reference in New Issue
Block a user