mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-14 15:15:22 +03:00
better date handling
This commit is contained in:
parent
eb6d2c8983
commit
89f8f69948
@ -5,8 +5,8 @@ import {
|
||||
LuImage,
|
||||
LuChevronDown,
|
||||
LuChevronUp,
|
||||
LuSave,
|
||||
LuTrash2,
|
||||
LuStar,
|
||||
} from "react-icons/lu";
|
||||
import {
|
||||
FilterType,
|
||||
@ -28,23 +28,8 @@ import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||
import { usePersistence } from "@/hooks/use-persistence";
|
||||
import { SaveSearchDialog } from "./SaveSearchDialog";
|
||||
import { DeleteSearchDialog } from "./DeleteSearchDialog";
|
||||
|
||||
const convertMMDDYYToTimestamp = (dateString: string): number => {
|
||||
const match = dateString.match(/^(\d{2})(\d{2})(\d{2})$/);
|
||||
if (!match) return 0;
|
||||
|
||||
const [, month, day, year] = match;
|
||||
const date = new Date(`20${year}-${month}-${day}T00:00:00Z`);
|
||||
return date.getTime();
|
||||
};
|
||||
|
||||
const emptyObject = Object.freeze([
|
||||
{
|
||||
name: "",
|
||||
search: "",
|
||||
filter: undefined,
|
||||
},
|
||||
]) as SavedSearchQuery[];
|
||||
import { convertLocalDateToTimestamp } from "@/utils/dateUtil";
|
||||
import { toast } from "sonner";
|
||||
|
||||
type InputWithTagsProps = {
|
||||
filters: SearchFilter;
|
||||
@ -76,7 +61,7 @@ export default function InputWithTags({
|
||||
|
||||
const [searchHistory, setSearchHistory, searchHistoryLoaded] = usePersistence<
|
||||
SavedSearchQuery[]
|
||||
>("frigate-search-history", emptyObject);
|
||||
>("frigate-search-history");
|
||||
|
||||
const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false);
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
@ -198,9 +183,39 @@ export default function InputWithTags({
|
||||
switch (type) {
|
||||
case "before":
|
||||
case "after":
|
||||
timestamp = convertMMDDYYToTimestamp(value);
|
||||
timestamp = convertLocalDateToTimestamp(value);
|
||||
if (timestamp > 0) {
|
||||
newFilters[type] = timestamp;
|
||||
// Check for conflicts with existing before/after filters
|
||||
if (
|
||||
type === "before" &&
|
||||
filters.after &&
|
||||
timestamp <= filters.after * 1000
|
||||
) {
|
||||
toast.error(
|
||||
"The 'before' date must be later than the 'after' date.",
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
type === "after" &&
|
||||
filters.before &&
|
||||
timestamp >= filters.before * 1000
|
||||
) {
|
||||
toast.error(
|
||||
"The 'after' date must be earlier than the 'before' date.",
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (type === "before") {
|
||||
timestamp -= 1;
|
||||
}
|
||||
newFilters[type] = timestamp / 1000;
|
||||
}
|
||||
break;
|
||||
case "search_type":
|
||||
@ -247,7 +262,7 @@ export default function InputWithTags({
|
||||
trimmedValue,
|
||||
) ||
|
||||
((filterType === "before" || filterType === "after") &&
|
||||
trimmedValue.match(/^\d{6}$/))
|
||||
trimmedValue.match(/^\d{8}$/))
|
||||
) {
|
||||
createFilter(filterType, trimmedValue);
|
||||
setInputValue((prev) => {
|
||||
@ -295,7 +310,7 @@ export default function InputWithTags({
|
||||
|
||||
if (filterType === "before" || filterType === "after") {
|
||||
// For before and after, we don't need to update suggestions
|
||||
if (filterValue.match(/^\d{6}$/)) {
|
||||
if (filterValue.match(/^\d{8}$/)) {
|
||||
handleFilterCreation(filterType, filterValue);
|
||||
}
|
||||
} else {
|
||||
@ -414,7 +429,11 @@ export default function InputWithTags({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Command shouldFilter={false} ref={commandRef} className="rounded-md">
|
||||
<Command
|
||||
shouldFilter={false}
|
||||
ref={commandRef}
|
||||
className="rounded-md border"
|
||||
>
|
||||
<div className="relative">
|
||||
<CommandInput
|
||||
ref={inputRef}
|
||||
@ -444,7 +463,7 @@ export default function InputWithTags({
|
||||
{(search || Object.keys(filters).length > 0) && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<LuSave
|
||||
<LuStar
|
||||
className="size-4 cursor-pointer text-secondary-foreground"
|
||||
onClick={handleSetSearchHistory}
|
||||
/>
|
||||
@ -550,7 +569,13 @@ export default function InputWithTags({
|
||||
>
|
||||
{filterType}:
|
||||
{filterType === "before" || filterType === "after"
|
||||
? new Date(filterValues as number).toLocaleDateString()
|
||||
? new Date(
|
||||
(filterType === "before"
|
||||
? (filterValues as number) + 1
|
||||
: (filterValues as number)) * 1000,
|
||||
).toLocaleDateString(
|
||||
window.navigator?.language || "en-US",
|
||||
)
|
||||
: filterValues}
|
||||
<button
|
||||
onClick={() =>
|
||||
@ -573,8 +598,9 @@ export default function InputWithTags({
|
||||
|
||||
{!currentFilterType &&
|
||||
!inputValue &&
|
||||
searchHistoryLoaded &&
|
||||
(searchHistory?.length ?? 0) > 0 && (
|
||||
<CommandGroup heading="Previous Searches">
|
||||
<CommandGroup heading="Saved Searches">
|
||||
{searchHistory?.map((suggestion, index) => (
|
||||
<CommandItem
|
||||
key={index}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useState } from "react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
type SaveSearchDialogProps = {
|
||||
isOpen: boolean;
|
||||
@ -34,7 +35,13 @@ export function SaveSearchDialog({
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent>
|
||||
<DialogContent
|
||||
onOpenAutoFocus={(e) => {
|
||||
if (isMobile) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Save Search</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
@ -43,14 +50,19 @@ export function SaveSearchDialog({
|
||||
</DialogHeader>
|
||||
<Input
|
||||
value={searchName}
|
||||
className="text-md"
|
||||
onChange={(e) => setSearchName(e.target.value)}
|
||||
placeholder="Enter a name for your search"
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button onClick={onClose} variant="select">
|
||||
Cancel
|
||||
<Button onClick={onClose}>Cancel</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
variant="select"
|
||||
className="mb-2 md:mb-0"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button onClick={handleSave}>Save</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -296,3 +296,63 @@ export function isCurrentHour(timestamp: number) {
|
||||
|
||||
return timestamp > now.getTime() / 1000;
|
||||
}
|
||||
|
||||
export const convertLocalDateToTimestamp = (dateString: string): number => {
|
||||
// Ensure the date string is in the correct format (8 digits)
|
||||
if (!/^\d{8}$/.test(dateString)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine the local date format
|
||||
const format = new Intl.DateTimeFormat()
|
||||
.formatToParts(new Date())
|
||||
.reduce((acc, part) => {
|
||||
if (part.type === "day") acc.push("D");
|
||||
if (part.type === "month") acc.push("M");
|
||||
if (part.type === "year") acc.push("Y");
|
||||
return acc;
|
||||
}, [] as string[])
|
||||
.join("");
|
||||
|
||||
let day: string, month: string, year: string;
|
||||
|
||||
// Parse the date string according to the detected format
|
||||
switch (format) {
|
||||
case "DMY":
|
||||
[day, month, year] = [
|
||||
dateString.slice(0, 2),
|
||||
dateString.slice(2, 4),
|
||||
dateString.slice(4),
|
||||
];
|
||||
break;
|
||||
case "MDY":
|
||||
[month, day, year] = [
|
||||
dateString.slice(0, 2),
|
||||
dateString.slice(2, 4),
|
||||
dateString.slice(4),
|
||||
];
|
||||
break;
|
||||
case "YMD":
|
||||
[year, month, day] = [
|
||||
dateString.slice(0, 2),
|
||||
dateString.slice(2, 4),
|
||||
dateString.slice(4),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a Date object based on the local timezone
|
||||
const localDate = new Date(`${year}-${month}-${day}T00:00:00`);
|
||||
|
||||
// Check if the date is valid
|
||||
if (isNaN(localDate.getTime())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert local date to UTC timestamp
|
||||
const timestamp = localDate.getTime();
|
||||
|
||||
return timestamp;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user