Add ability to use 12 hour time in input time filter (#13961)

This commit is contained in:
Josh Hawkins 2024-09-25 12:15:08 -05:00 committed by GitHub
parent 45aceea53b
commit be3e1831d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 91 additions and 44 deletions

View File

@ -38,6 +38,7 @@ import {
convertTo12Hour,
getIntlDateFormat,
isValidTimeRange,
to24Hour,
} from "@/utils/dateUtil";
import { toast } from "sonner";
import useSWR from "swr";
@ -191,7 +192,8 @@ export default function InputWithTags({
if (
allSuggestions[type as FilterType]?.includes(value) ||
type == "before" ||
type == "after"
type == "after" ||
type == "time_range"
) {
const newFilters = { ...filters };
let timestamp = 0;
@ -235,23 +237,6 @@ export default function InputWithTags({
}
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":
@ -299,7 +284,9 @@ export default function InputWithTags({
: (filterValues as number)) * 1000,
).toLocaleDateString(window.navigator?.language || "en-US");
} else if (filterType === "time_range") {
const [startTime, endTime] = (filterValues as string).split(",");
const [startTime, endTime] = (filterValues as string)
.replace("-", ",")
.split(",");
return `${
config?.ui.time_format === "24hour"
? startTime
@ -320,9 +307,23 @@ export default function InputWithTags({
if (
allSuggestions[filterType]?.includes(trimmedValue) ||
((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) => {
const regex = new RegExp(
`${filterType}:${filterValue.trim()}[,\\s]*`,
@ -335,7 +336,7 @@ export default function InputWithTags({
setCurrentFilterType(null);
}
},
[allSuggestions, createFilter],
[allSuggestions, createFilter, config],
);
const handleInputChange = useCallback(
@ -362,18 +363,11 @@ export default function InputWithTags({
if (filterType in allSuggestions) {
setCurrentFilterType(filterType);
if (filterType === "before" || filterType === "after") {
// For before and after, we don't need to update suggestions
if (filterValue.match(/^\d{8}$/)) {
handleFilterCreation(filterType, filterValue);
}
} else {
updateSuggestions(filterValue, filterType);
updateSuggestions(filterValue, filterType);
// Check if the last character is a space or comma
if (isLastCharSpaceOrComma) {
handleFilterCreation(filterType, filterValue);
}
// Check if the last character is a space or comma
if (isLastCharSpaceOrComma) {
handleFilterCreation(filterType, filterValue);
}
} else {
resetSuggestions(value);
@ -413,6 +407,13 @@ export default function InputWithTags({
(suggestion: string) => {
if (currentFilterType) {
// 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);
setInputValue((prev) => {
const regex = new RegExp(`${currentFilterType}:[^\\s,]*`, "g");
@ -439,7 +440,7 @@ export default function InputWithTags({
inputRef.current?.focus();
},
[createFilter, currentFilterType, allSuggestions],
[createFilter, currentFilterType, allSuggestions, config],
);
const handleSearch = useCallback(
@ -565,7 +566,7 @@ export default function InputWithTags({
<h3 className="font-medium">How to use text filters</h3>
<p className="text-sm text-muted-foreground">
Filters help you narrow down your search results. Here's how
to use them:
to use them in the input field:
</p>
<ul className="list-disc pl-5 text-sm text-primary-variant">
<li>
@ -575,11 +576,21 @@ export default function InputWithTags({
Select a value from the suggestions or type your own.
</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>
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>Remove filters by clicking the 'x' next to them.</li>
</ul>
@ -587,6 +598,7 @@ export default function InputWithTags({
Example:{" "}
<code className="text-primary">
cameras:front_door label:person before:01012024
time_range:3:00PM-4:00PM
</code>
</p>
</div>

View File

@ -387,22 +387,54 @@ export function formatDateToLocaleString(daysOffset: number = 0): string {
.replace(/[^\d]/g, "");
}
export function isValidTimeRange(rangeString: string): boolean {
const range = rangeString.split(",");
export function to24Hour(
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) {
return false;
}
const is24HourFormat = time_format === "24hour";
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;
};
const isValidTime = (time: string): boolean =>
/^(?:([01]\d|2[0-3]):([0-5]\d)|24:00)$/.test(time);
const isValidTime = (time: string): boolean => {
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 (
isValidTime(startTime) &&

View File

@ -121,7 +121,10 @@ export default function SearchView({
zones: Object.values(allZones || {}),
sub_labels: allSubLabels,
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()],
after: [formatDateToLocaleString(-5)],
}),