mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-12-23 19:11:14 +01:00
Add ability to use 12 hour time in input time filter (#13961)
This commit is contained in:
parent
45aceea53b
commit
be3e1831d4
@ -38,6 +38,7 @@ import {
|
|||||||
convertTo12Hour,
|
convertTo12Hour,
|
||||||
getIntlDateFormat,
|
getIntlDateFormat,
|
||||||
isValidTimeRange,
|
isValidTimeRange,
|
||||||
|
to24Hour,
|
||||||
} from "@/utils/dateUtil";
|
} from "@/utils/dateUtil";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -191,7 +192,8 @@ export default function InputWithTags({
|
|||||||
if (
|
if (
|
||||||
allSuggestions[type as FilterType]?.includes(value) ||
|
allSuggestions[type as FilterType]?.includes(value) ||
|
||||||
type == "before" ||
|
type == "before" ||
|
||||||
type == "after"
|
type == "after" ||
|
||||||
|
type == "time_range"
|
||||||
) {
|
) {
|
||||||
const newFilters = { ...filters };
|
const newFilters = { ...filters };
|
||||||
let timestamp = 0;
|
let timestamp = 0;
|
||||||
@ -235,23 +237,6 @@ export default function InputWithTags({
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "time_range":
|
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;
|
newFilters[type] = value;
|
||||||
break;
|
break;
|
||||||
case "search_type":
|
case "search_type":
|
||||||
@ -299,7 +284,9 @@ export default function InputWithTags({
|
|||||||
: (filterValues as number)) * 1000,
|
: (filterValues as number)) * 1000,
|
||||||
).toLocaleDateString(window.navigator?.language || "en-US");
|
).toLocaleDateString(window.navigator?.language || "en-US");
|
||||||
} else if (filterType === "time_range") {
|
} else if (filterType === "time_range") {
|
||||||
const [startTime, endTime] = (filterValues as string).split(",");
|
const [startTime, endTime] = (filterValues as string)
|
||||||
|
.replace("-", ",")
|
||||||
|
.split(",");
|
||||||
return `${
|
return `${
|
||||||
config?.ui.time_format === "24hour"
|
config?.ui.time_format === "24hour"
|
||||||
? startTime
|
? startTime
|
||||||
@ -320,9 +307,23 @@ export default function InputWithTags({
|
|||||||
if (
|
if (
|
||||||
allSuggestions[filterType]?.includes(trimmedValue) ||
|
allSuggestions[filterType]?.includes(trimmedValue) ||
|
||||||
((filterType === "before" || filterType === "after") &&
|
((filterType === "before" || filterType === "after") &&
|
||||||
trimmedValue.match(/^\d{8}$/))
|
trimmedValue.match(/^\d{8}$/)) ||
|
||||||
|
(filterType === "time_range" &&
|
||||||
|
isValidTimeRange(
|
||||||
|
trimmedValue.replace("-", ","),
|
||||||
|
config?.ui.time_format,
|
||||||
|
))
|
||||||
) {
|
) {
|
||||||
createFilter(filterType, trimmedValue);
|
createFilter(
|
||||||
|
filterType,
|
||||||
|
filterType === "time_range"
|
||||||
|
? trimmedValue
|
||||||
|
.replace("-", ",")
|
||||||
|
.split(",")
|
||||||
|
.map((time) => to24Hour(time.trim(), config?.ui.time_format))
|
||||||
|
.join(",")
|
||||||
|
: trimmedValue,
|
||||||
|
);
|
||||||
setInputValue((prev) => {
|
setInputValue((prev) => {
|
||||||
const regex = new RegExp(
|
const regex = new RegExp(
|
||||||
`${filterType}:${filterValue.trim()}[,\\s]*`,
|
`${filterType}:${filterValue.trim()}[,\\s]*`,
|
||||||
@ -335,7 +336,7 @@ export default function InputWithTags({
|
|||||||
setCurrentFilterType(null);
|
setCurrentFilterType(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[allSuggestions, createFilter],
|
[allSuggestions, createFilter, config],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInputChange = useCallback(
|
const handleInputChange = useCallback(
|
||||||
@ -362,18 +363,11 @@ export default function InputWithTags({
|
|||||||
if (filterType in allSuggestions) {
|
if (filterType in allSuggestions) {
|
||||||
setCurrentFilterType(filterType);
|
setCurrentFilterType(filterType);
|
||||||
|
|
||||||
if (filterType === "before" || filterType === "after") {
|
updateSuggestions(filterValue, filterType);
|
||||||
// For before and after, we don't need to update suggestions
|
|
||||||
if (filterValue.match(/^\d{8}$/)) {
|
|
||||||
handleFilterCreation(filterType, filterValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateSuggestions(filterValue, filterType);
|
|
||||||
|
|
||||||
// Check if the last character is a space or comma
|
// Check if the last character is a space or comma
|
||||||
if (isLastCharSpaceOrComma) {
|
if (isLastCharSpaceOrComma) {
|
||||||
handleFilterCreation(filterType, filterValue);
|
handleFilterCreation(filterType, filterValue);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetSuggestions(value);
|
resetSuggestions(value);
|
||||||
@ -413,6 +407,13 @@ export default function InputWithTags({
|
|||||||
(suggestion: string) => {
|
(suggestion: string) => {
|
||||||
if (currentFilterType) {
|
if (currentFilterType) {
|
||||||
// Apply the selected suggestion to the current filter type
|
// Apply the selected suggestion to the current filter type
|
||||||
|
if (currentFilterType == "time_range") {
|
||||||
|
suggestion = suggestion
|
||||||
|
.replace("-", ",")
|
||||||
|
.split(",")
|
||||||
|
.map((time) => to24Hour(time.trim(), config?.ui.time_format))
|
||||||
|
.join(",");
|
||||||
|
}
|
||||||
createFilter(currentFilterType, suggestion);
|
createFilter(currentFilterType, suggestion);
|
||||||
setInputValue((prev) => {
|
setInputValue((prev) => {
|
||||||
const regex = new RegExp(`${currentFilterType}:[^\\s,]*`, "g");
|
const regex = new RegExp(`${currentFilterType}:[^\\s,]*`, "g");
|
||||||
@ -439,7 +440,7 @@ export default function InputWithTags({
|
|||||||
|
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
},
|
},
|
||||||
[createFilter, currentFilterType, allSuggestions],
|
[createFilter, currentFilterType, allSuggestions, config],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
@ -565,7 +566,7 @@ export default function InputWithTags({
|
|||||||
<h3 className="font-medium">How to use text filters</h3>
|
<h3 className="font-medium">How to use text filters</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Filters help you narrow down your search results. Here's how
|
Filters help you narrow down your search results. Here's how
|
||||||
to use them:
|
to use them in the input field:
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc pl-5 text-sm text-primary-variant">
|
<ul className="list-disc pl-5 text-sm text-primary-variant">
|
||||||
<li>
|
<li>
|
||||||
@ -575,11 +576,21 @@ export default function InputWithTags({
|
|||||||
Select a value from the suggestions or type your own.
|
Select a value from the suggestions or type your own.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Use multiple filters by adding them one after another.
|
Use multiple filters by adding them one after another with
|
||||||
|
a space in between.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Date filters (before: and after:) use{" "}
|
Date filters (before: and after:) use{" "}
|
||||||
{getIntlDateFormat()} format.
|
<em>{getIntlDateFormat()}</em> format.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Time range filter uses{" "}
|
||||||
|
<em>
|
||||||
|
{config?.ui.time_format == "24hour"
|
||||||
|
? "15:00-16:00"
|
||||||
|
: "3:00PM-4:00PM"}{" "}
|
||||||
|
</em>
|
||||||
|
format.
|
||||||
</li>
|
</li>
|
||||||
<li>Remove filters by clicking the 'x' next to them.</li>
|
<li>Remove filters by clicking the 'x' next to them.</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -587,6 +598,7 @@ export default function InputWithTags({
|
|||||||
Example:{" "}
|
Example:{" "}
|
||||||
<code className="text-primary">
|
<code className="text-primary">
|
||||||
cameras:front_door label:person before:01012024
|
cameras:front_door label:person before:01012024
|
||||||
|
time_range:3:00PM-4:00PM
|
||||||
</code>
|
</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -387,22 +387,54 @@ export function formatDateToLocaleString(daysOffset: number = 0): string {
|
|||||||
.replace(/[^\d]/g, "");
|
.replace(/[^\d]/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidTimeRange(rangeString: string): boolean {
|
export function to24Hour(
|
||||||
const range = rangeString.split(",");
|
time: string,
|
||||||
|
time_format: "12hour" | "24hour" | "browser" = "24hour",
|
||||||
|
): string {
|
||||||
|
const is24HourFormat = time_format === "24hour";
|
||||||
|
|
||||||
|
if (is24HourFormat) return time;
|
||||||
|
|
||||||
|
const [timePart, ampm] = time.split(/([AP]M)/i);
|
||||||
|
|
||||||
|
if (!timePart || !ampm) {
|
||||||
|
throw new Error(`Invalid time format: ${time}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hours = Number(timePart.split(":")[0]);
|
||||||
|
const minutes = Number(timePart.split(":")[1]);
|
||||||
|
|
||||||
|
if (ampm.toUpperCase() === "PM" && hours !== 12) hours += 12;
|
||||||
|
if (ampm.toUpperCase() === "AM" && hours === 12) hours = 0;
|
||||||
|
|
||||||
|
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidTimeRange(
|
||||||
|
rangeString: string,
|
||||||
|
time_format?: "12hour" | "24hour" | "browser",
|
||||||
|
): boolean {
|
||||||
|
const range = rangeString.split(",");
|
||||||
if (range.length !== 2) {
|
if (range.length !== 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const is24HourFormat = time_format === "24hour";
|
||||||
|
|
||||||
const toMinutes = (time: string): number => {
|
const toMinutes = (time: string): number => {
|
||||||
const [h, m] = time.split(":").map(Number);
|
const [h, m] = to24Hour(time, time_format).split(":").map(Number);
|
||||||
return h * 60 + m;
|
return h * 60 + m;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidTime = (time: string): boolean =>
|
const isValidTime = (time: string): boolean => {
|
||||||
/^(?:([01]\d|2[0-3]):([0-5]\d)|24:00)$/.test(time);
|
if (is24HourFormat) {
|
||||||
|
return /^(?:([01]\d|2[0-3]):([0-5]\d)|24:00)$/.test(time);
|
||||||
|
} else {
|
||||||
|
return /^(0?[1-9]|1[0-2]):[0-5][0-9](A|P)M$/i.test(time);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const [startTime, endTime] = range;
|
const [startTime, endTime] = range.map((t) => t.trim());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isValidTime(startTime) &&
|
isValidTime(startTime) &&
|
||||||
|
@ -121,7 +121,10 @@ 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"],
|
time_range:
|
||||||
|
config?.ui.time_format == "24hour"
|
||||||
|
? ["00:00-23:59"]
|
||||||
|
: ["12:00AM-11:59PM"],
|
||||||
before: [formatDateToLocaleString()],
|
before: [formatDateToLocaleString()],
|
||||||
after: [formatDateToLocaleString(-5)],
|
after: [formatDateToLocaleString(-5)],
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user