Add ability to filter search by time range (#13946)

* Add ability to filter by time range

* Cleanup

* Handle input with tags

* fix input for time_range filter

* fix before and after filters

* clean up

* Ensure the default value works as expected

* Handle time range in am/pm based on browser

* Fix arrow

* Fix text

* Handle midnight case

* fix width

* Fix bg

* Fix bg

* Fix mobile spacing

* y spacing

* remove left padding

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
Nicolas Mowen
2024-09-25 10:05:40 -06:00
committed by GitHub
parent 4c24b70d47
commit 25819584bd
11 changed files with 501 additions and 185 deletions

View File

@@ -35,9 +35,13 @@ import { SaveSearchDialog } from "./SaveSearchDialog";
import { DeleteSearchDialog } from "./DeleteSearchDialog";
import {
convertLocalDateToTimestamp,
convertTo12Hour,
getIntlDateFormat,
isValidTimeRange,
} from "@/utils/dateUtil";
import { toast } from "sonner";
import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig";
type InputWithTagsProps = {
filters: SearchFilter;
@@ -56,6 +60,10 @@ export default function InputWithTags({
setSearch,
allSuggestions,
}: InputWithTagsProps) {
const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
const [inputValue, setInputValue] = useState(search || "");
const [currentFilterType, setCurrentFilterType] = useState<FilterType | null>(
null,
@@ -180,7 +188,11 @@ export default function InputWithTags({
const createFilter = useCallback(
(type: FilterType, value: string) => {
if (allSuggestions[type as FilterType]?.includes(value)) {
if (
allSuggestions[type as FilterType]?.includes(value) ||
type == "before" ||
type == "after"
) {
const newFilters = { ...filters };
let timestamp = 0;
@@ -222,6 +234,26 @@ export default function InputWithTags({
newFilters[type] = timestamp / 1000;
}
break;
case "time_range":
if (!value.includes(",")) {
toast.error(
"The correct format is after,before. Example: 15:00,18:00.",
{
position: "top-center",
},
);
return;
}
if (!isValidTimeRange(value)) {
toast.error("Time range is not valid.", {
position: "top-center",
});
return;
}
newFilters[type] = value;
break;
case "search_type":
if (!newFilters.search_type) newFilters.search_type = [];
if (
@@ -256,6 +288,30 @@ export default function InputWithTags({
[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
const handleFilterCreation = useCallback(
@@ -303,11 +359,7 @@ export default function InputWithTags({
];
// Check if filter type is valid
if (
filterType in allSuggestions ||
filterType === "before" ||
filterType === "after"
) {
if (filterType in allSuggestions) {
setCurrentFilterType(filterType);
if (filterType === "before" || filterType === "after") {
@@ -604,16 +656,8 @@ export default function InputWithTags({
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"
>
{filterType}:
{filterType === "before" || filterType === "after"
? new Date(
(filterType === "before"
? (filterValues as number) + 1
: (filterValues as number)) * 1000,
).toLocaleDateString(
window.navigator?.language || "en-US",
)
: filterValues}
{filterType.replaceAll("_", " ")}:{" "}
{formatFilterValues(filterType, filterValues)}
<button
onClick={() =>
removeFilter(