diff --git a/.devcontainer/post_create.sh b/.devcontainer/post_create.sh old mode 100755 new mode 100644 diff --git a/web/public/locales/en/common.json b/web/public/locales/en/common.json index 272e0f2795..a6e6daa1e1 100644 --- a/web/public/locales/en/common.json +++ b/web/public/locales/en/common.json @@ -21,6 +21,7 @@ "1hour": "1 hour", "12hours": "12 hours", "24hours": "24 hours", + "custom": "Custom...", "pm": "pm", "am": "am", "yr": "{{time}}yr", diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index e4bd7ae517..dc96d92671 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -1191,8 +1191,15 @@ "1hour": "Suspend for 1 hour", "12hours": "Suspend for 12 hours", "24hours": "Suspend for 24 hours", + "custom": "Suspend until...", "untilRestart": "Suspend until restart" }, + "customSuspension": { + "title": "Custom suspension time", + "description": "Suspend notifications for this camera until the selected time.", + "untilLabel": "Suspend until", + "invalidTime": "Pick a time in the future." + }, "cancelSuspension": "Cancel Suspension", "toast": { "success": { diff --git a/web/src/components/config-form/sectionExtras/NotificationsSettingsExtras.tsx b/web/src/components/config-form/sectionExtras/NotificationsSettingsExtras.tsx index 337f37f23a..7a560af9f2 100644 --- a/web/src/components/config-form/sectionExtras/NotificationsSettingsExtras.tsx +++ b/web/src/components/config-form/sectionExtras/NotificationsSettingsExtras.tsx @@ -23,7 +23,7 @@ import { useState, } from "react"; import { useForm } from "react-hook-form"; -import { LuCheck, LuExternalLink, LuX } from "react-icons/lu"; +import { LuCheck, LuChevronDown, LuExternalLink, LuX } from "react-icons/lu"; import { CiCircleAlert } from "react-icons/ci"; import { Link } from "react-router-dom"; import { toast } from "sonner"; @@ -35,12 +35,12 @@ import { useNotificationTest, } from "@/api/ws"; import { - Select, - SelectTrigger, - SelectValue, - SelectContent, - SelectItem, -} from "@/components/ui/select"; + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { use24HourTime } from "@/hooks/use-date-utils"; import FilterSwitch from "@/components/filter/FilterSwitch"; @@ -51,6 +51,7 @@ import { useDocDomain } from "@/hooks/use-doc-domain"; import { isPWA } from "@/utils/isPWA"; import { isIOS } from "react-device-detect"; import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel"; +import CustomSuspensionDialog from "@/components/overlay/dialog/CustomSuspensionDialog"; import { useIsAdmin } from "@/hooks/use-is-admin"; import { cn } from "@/lib/utils"; import cloneDeep from "lodash/cloneDeep"; @@ -756,6 +757,8 @@ export function CameraNotificationSwitch({ } }, [notificationSuspendUntil, notificationState]); + const [customDialogOpen, setCustomDialogOpen] = useState(false); + const handleSuspend = (duration: string) => { setIsSuspended(true); if (duration == "off") { @@ -765,6 +768,11 @@ export function CameraNotificationSwitch({ } }; + const handleCustomSuspend = (totalMinutes: number) => { + setIsSuspended(true); + sendNotificationSuspend(totalMinutes); + }; + const handleCancelSuspension = () => { sendNotification("ON"); sendNotificationSuspend(0); @@ -824,34 +832,41 @@ export function CameraNotificationSwitch({ {!isSuspended ? ( - + + + setCustomDialogOpen(true)}> + {t("notification.suspendTime.custom")} + + + ) : ( + + + { + if (!day) return; + const next = new Date(day); + const carry = isValidDate(until) ? until : new Date(); + next.setHours( + carry.getHours(), + carry.getMinutes(), + carry.getSeconds(), + 0, + ); + setUntil(next); + setCalendarOpen(false); + }} + /> + + + { + const [h, m] = e.target.value.split(":"); + const hh = Number.parseInt(h ?? "", 10); + const mm = Number.parseInt(m ?? "", 10); + if (Number.isNaN(hh) || Number.isNaN(mm)) return; + const base = isValidDate(until) ? until : new Date(); + const next = new Date(base); + next.setHours(hh, mm, 0, 0); + setUntil(next); + }} + /> + + {!isFuture && ( +

+ {t("notification.customSuspension.invalidTime")} +

+ )} + + + + + + + + + ); +}