fix input for time_range filter

This commit is contained in:
Josh Hawkins 2024-09-25 07:59:27 -05:00
parent c0c81ef3f2
commit a33bc563bc
6 changed files with 88 additions and 29 deletions

View File

@ -172,9 +172,9 @@ export default function SearchFilterGroup({
)} )}
{filters.includes("time") && ( {filters.includes("time") && (
<TimeRangeFilterButton <TimeRangeFilterButton
timeRange={filter?.timeRange} timeRange={filter?.time_range}
updateTimeRange={(timeRange) => updateTimeRange={(time_range) =>
onUpdateFilter({ ...filter, timeRange }) onUpdateFilter({ ...filter, time_range })
} }
/> />
)} )}
@ -199,9 +199,9 @@ export default function SearchFilterGroup({
{filters.includes("sub") && ( {filters.includes("sub") && (
<SubFilterButton <SubFilterButton
allSubLabels={allSubLabels} allSubLabels={allSubLabels}
selectedSubLabels={filter?.subLabels} selectedSubLabels={filter?.sub_labels}
updateSubLabelFilter={(newSubLabels) => updateSubLabelFilter={(newSubLabels) =>
onUpdateFilter({ ...filter, subLabels: newSubLabels }) onUpdateFilter({ ...filter, sub_labels: newSubLabels })
} }
/> />
)} )}

View File

@ -35,9 +35,13 @@ import { SaveSearchDialog } from "./SaveSearchDialog";
import { DeleteSearchDialog } from "./DeleteSearchDialog"; import { DeleteSearchDialog } from "./DeleteSearchDialog";
import { import {
convertLocalDateToTimestamp, convertLocalDateToTimestamp,
convertTo12Hour,
getIntlDateFormat, getIntlDateFormat,
isValidTimeRange,
} from "@/utils/dateUtil"; } from "@/utils/dateUtil";
import { toast } from "sonner"; import { toast } from "sonner";
import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig";
type InputWithTagsProps = { type InputWithTagsProps = {
filters: SearchFilter; filters: SearchFilter;
@ -56,6 +60,10 @@ export default function InputWithTags({
setSearch, setSearch,
allSuggestions, allSuggestions,
}: InputWithTagsProps) { }: InputWithTagsProps) {
const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
const [inputValue, setInputValue] = useState(search || ""); const [inputValue, setInputValue] = useState(search || "");
const [currentFilterType, setCurrentFilterType] = useState<FilterType | null>( const [currentFilterType, setCurrentFilterType] = useState<FilterType | null>(
null, null,
@ -183,7 +191,6 @@ export default function InputWithTags({
if (allSuggestions[type as FilterType]?.includes(value)) { if (allSuggestions[type as FilterType]?.includes(value)) {
const newFilters = { ...filters }; const newFilters = { ...filters };
let timestamp = 0; let timestamp = 0;
let times = ["", ""];
switch (type) { switch (type) {
case "before": case "before":
@ -223,18 +230,19 @@ export default function InputWithTags({
newFilters[type] = timestamp / 1000; newFilters[type] = timestamp / 1000;
} }
break; break;
case "timeRange": case "time_range":
if (!value.includes(",")) { if (!value.includes(",")) {
toast.error("The correct format is after,before.", { toast.error(
"The correct format is after,before. Example: 15:00,18:00.",
{
position: "top-center", position: "top-center",
}); },
);
return; return;
} }
times = value.split(","); if (!isValidTimeRange(value)) {
toast.error("Time range is not valid.", {
if (times[0] < "00" || times[1] > "24") {
toast.error("Times not in valid range", {
position: "top-center", position: "top-center",
}); });
return; return;
@ -276,6 +284,30 @@ export default function InputWithTags({
[filters, setFilters, allSuggestions], [filters, setFilters, allSuggestions],
); );
function formatFilterValues(
filterType: string,
filterValues: number | string,
): string {
if (filterType === "before" || filterType === "after") {
return new Date(
(filterType === "before"
? (filterValues as number) + 1
: (filterValues as number)) * 1000,
).toLocaleDateString(window.navigator?.language || "en-US");
} else if (filterType === "time_range") {
const [startTime, endTime] = (filterValues as string).split(",");
return `${
config?.ui.time_format === "24hour"
? startTime
: convertTo12Hour(startTime)
} - ${
config?.ui.time_format === "24hour" ? endTime : convertTo12Hour(endTime)
}`;
} else {
return filterValues as string;
}
}
// handlers // handlers
const handleFilterCreation = useCallback( const handleFilterCreation = useCallback(
@ -624,16 +656,8 @@ export default function InputWithTags({
key={filterType} key={filterType}
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800" className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
> >
{filterType}: {filterType.replaceAll("_", " ")}:{" "}
{filterType === "before" || filterType === "after" {formatFilterValues(filterType, filterValues)}
? new Date(
(filterType === "before"
? (filterValues as number) + 1
: (filterValues as number)) * 1000,
).toLocaleDateString(
window.navigator?.language || "en-US",
)
: filterValues}
<button <button
onClick={() => onClick={() =>
removeFilter( removeFilter(

View File

@ -70,11 +70,11 @@ export default function Explore() {
{ {
cameras: searchSearchParams["cameras"], cameras: searchSearchParams["cameras"],
labels: searchSearchParams["labels"], labels: searchSearchParams["labels"],
sub_labels: searchSearchParams["subLabels"], sub_labels: searchSearchParams["sub_labels"],
zones: searchSearchParams["zones"], zones: searchSearchParams["zones"],
before: searchSearchParams["before"], before: searchSearchParams["before"],
after: searchSearchParams["after"], after: searchSearchParams["after"],
time_range: searchSearchParams["timeRange"], time_range: searchSearchParams["time_range"],
search_type: searchSearchParams["search_type"], search_type: searchSearchParams["search_type"],
limit: limit:
Object.keys(searchSearchParams).length == 0 ? API_LIMIT : undefined, Object.keys(searchSearchParams).length == 0 ? API_LIMIT : undefined,
@ -96,10 +96,11 @@ export default function Explore() {
query: similaritySearch ? undefined : searchTerm, query: similaritySearch ? undefined : searchTerm,
cameras: searchSearchParams["cameras"], cameras: searchSearchParams["cameras"],
labels: searchSearchParams["labels"], labels: searchSearchParams["labels"],
sub_labels: searchSearchParams["subLabels"], sub_labels: searchSearchParams["sub_labels"],
zones: searchSearchParams["zones"], zones: searchSearchParams["zones"],
before: searchSearchParams["before"], before: searchSearchParams["before"],
after: searchSearchParams["after"], after: searchSearchParams["after"],
time_range: searchSearchParams["time_range"],
search_type: searchSearchParams["search_type"], search_type: searchSearchParams["search_type"],
event_id: searchSearchParams["event_id"], event_id: searchSearchParams["event_id"],
include_thumbnails: 0, include_thumbnails: 0,

View File

@ -52,11 +52,11 @@ export type SearchFilter = {
query?: string; query?: string;
cameras?: string[]; cameras?: string[];
labels?: string[]; labels?: string[];
subLabels?: string[]; sub_labels?: string[];
zones?: string[]; zones?: string[];
before?: number; before?: number;
after?: number; after?: number;
timeRange?: string; time_range?: string;
search_type?: SearchSource[]; search_type?: SearchSource[];
event_id?: string; event_id?: string;
}; };
@ -74,6 +74,7 @@ export type SearchQueryParams = {
include_thumbnails?: number; include_thumbnails?: number;
query?: string; query?: string;
page?: number; page?: number;
time_range?: string;
}; };
export type SearchQuery = [string, SearchQueryParams] | null; export type SearchQuery = [string, SearchQueryParams] | null;

View File

@ -373,3 +373,35 @@ export function getIntlDateFormat() {
}, [] as string[]) }, [] as string[])
.join(""); .join("");
} }
export function isValidTimeRange(rangeString: string): boolean {
const range = rangeString.split(",");
if (range.length !== 2) {
return false;
}
const toMinutes = (time: string): number => {
const [h, m] = time.split(":").map(Number);
return h * 60 + m;
};
const isValidTime = (time: string): boolean =>
/^(?:([01]\d|2[0-3]):([0-5]\d)|24:00)$/.test(time);
const [startTime, endTime] = range;
return (
isValidTime(startTime) &&
isValidTime(endTime) &&
toMinutes(startTime) < toMinutes(endTime)
);
}
export function convertTo12Hour(time: string) {
const [hours, minutes] = time.split(":");
const hour = parseInt(hours, 10);
const ampm = hour >= 12 ? "PM" : "AM";
const hour12 = hour % 12 || 12;
return `${hour12}:${minutes} ${ampm}`;
}

View File

@ -120,6 +120,7 @@ export default function SearchView({
zones: Object.values(allZones || {}), zones: Object.values(allZones || {}),
sub_labels: allSubLabels, sub_labels: allSubLabels,
search_type: ["thumbnail", "description"] as SearchSource[], search_type: ["thumbnail", "description"] as SearchSource[],
time_range: ["00:00,24:00"],
}), }),
[config, allLabels, allZones, allSubLabels], [config, allLabels, allZones, allSubLabels],
); );