Compare commits

...

2 Commits

Author SHA1 Message Date
henrygd
1275af956b updates 2025-11-24 16:57:06 -05:00
henrygd
bf36015bd9 updates 2025-11-24 16:40:18 -05:00
5 changed files with 57 additions and 64 deletions

View File

@@ -153,25 +153,22 @@ func (am *AlertManager) IsNotificationSilenced(userID, systemID string) bool {
// Handle case where window crosses midnight // Handle case where window crosses midnight
if endMinutes < startMinutes { if endMinutes < startMinutes {
// Window crosses midnight (e.g., 23:00 - 01:00) // Window crosses midnight (e.g., 23:00 - 01:00)
if nowMinutes >= startMinutes || nowMinutes <= endMinutes { if nowMinutes >= startMinutes || nowMinutes < endMinutes {
return true return true
} }
} else { } else {
// Normal case (e.g., 09:00 - 17:00) // Normal case (e.g., 09:00 - 17:00)
if nowMinutes >= startMinutes && nowMinutes <= endMinutes { if nowMinutes >= startMinutes && nowMinutes < endMinutes {
return true return true
} }
} }
} else { } else {
// One-time window: check if current time is within the date range // One-time window: check if current time is within the date range
if now.After(start) || now.Equal(start) { if (now.After(start) || now.Equal(start)) && now.Before(end) {
// If end is zero/null, suppression continues indefinitely from start
if end.IsZero() || now.Before(end) || now.Equal(end) {
return true return true
} }
} }
} }
}
return false return false
} }

View File

@@ -1227,7 +1227,7 @@ func init() {
"min": "", "min": "",
"name": "end", "name": "end",
"presentable": false, "presentable": false,
"required": false, "required": true,
"system": false, "system": false,
"type": "date" "type": "date"
} }

View File

@@ -30,11 +30,12 @@
"noUnusedFunctionParameters": "error", "noUnusedFunctionParameters": "error",
"noUnusedPrivateClassMembers": "error", "noUnusedPrivateClassMembers": "error",
"useExhaustiveDependencies": { "useExhaustiveDependencies": {
"level": "error", "level": "warn",
"options": { "options": {
"reportUnnecessaryDependencies": false "reportUnnecessaryDependencies": false
} }
}, },
"useUniqueElementIds": "off",
"noUnusedVariables": "error" "noUnusedVariables": "error"
}, },
"style": { "style": {

View File

@@ -30,7 +30,7 @@ import { useToast } from "@/components/ui/use-toast"
import { pb } from "@/lib/api" import { pb } from "@/lib/api"
import { $systems } from "@/lib/stores" import { $systems } from "@/lib/stores"
import { formatShortDate } from "@/lib/utils" import { formatShortDate } from "@/lib/utils"
import type { QuietHoursRecord } from "@/types" import type { QuietHoursRecord, SystemRecord } from "@/types"
export function QuietHours() { export function QuietHours() {
const [data, setData] = useState<QuietHoursRecord[]>([]) const [data, setData] = useState<QuietHoursRecord[]>([])
@@ -80,11 +80,11 @@ export function QuietHours() {
const handleDelete = async (id: string) => { const handleDelete = async (id: string) => {
try { try {
await pb.collection("quiet_hours").delete(id) await pb.collection("quiet_hours").delete(id)
} catch (e: any) { } catch (e: unknown) {
toast({ toast({
variant: "destructive", variant: "destructive",
title: t`Error`, title: t`Error`,
description: e.message || "Failed to delete quiet hours.", description: (e as Error).message || "Failed to delete quiet hours.",
}) })
} }
} }
@@ -103,13 +103,13 @@ export function QuietHours() {
if (record.type === "daily") { if (record.type === "daily") {
// For daily windows, show only time // For daily windows, show only time
const startTime = new Date(record.start).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) const startTime = new Date(record.start).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
const endTime = record.end ? new Date(record.end).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "" const endTime = new Date(record.end).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
return endTime ? `${startTime} - ${endTime}` : startTime return `${startTime} - ${endTime}`
} }
// For one-time windows, show full date and time // For one-time windows, show full date and time
const start = formatShortDate(record.start) const start = formatShortDate(record.start)
const end = record.end ? formatShortDate(record.end) : "" const end = formatShortDate(record.end)
return end ? `${start} - ${end}` : start return `${start} - ${end}`
} }
const getWindowState = (record: QuietHoursRecord): "active" | "past" | "future" => { const getWindowState = (record: QuietHoursRecord): "active" | "past" | "future" => {
@@ -118,22 +118,17 @@ export function QuietHours() {
if (record.type === "daily") { if (record.type === "daily") {
// For daily windows, check if current time is within the window // For daily windows, check if current time is within the window
const startDate = new Date(record.start) const startDate = new Date(record.start)
const endDate = record.end ? new Date(record.end) : null const endDate = new Date(record.end)
// Get current time in local timezone // Get current time in local timezone
const currentMinutes = now.getHours() * 60 + now.getMinutes() const currentMinutes = now.getHours() * 60 + now.getMinutes()
const startMinutes = startDate.getUTCHours() * 60 + startDate.getUTCMinutes() const startMinutes = startDate.getUTCHours() * 60 + startDate.getUTCMinutes()
const endMinutes = endDate ? endDate.getUTCHours() * 60 + endDate.getUTCMinutes() : null const endMinutes = endDate.getUTCHours() * 60 + endDate.getUTCMinutes()
// Convert UTC to local time offset // Convert UTC to local time offset
const offset = now.getTimezoneOffset() const offset = now.getTimezoneOffset()
const localStartMinutes = (startMinutes - offset + 1440) % 1440 const localStartMinutes = (startMinutes - offset + 1440) % 1440
const localEndMinutes = endMinutes !== null ? (endMinutes - offset + 1440) % 1440 : null const localEndMinutes = (endMinutes - offset + 1440) % 1440
if (localEndMinutes === null) {
// No end time, so it's always active from start time onwards each day
return "active"
}
// Handle cases where window spans midnight // Handle cases where window spans midnight
if (localStartMinutes <= localEndMinutes) { if (localStartMinutes <= localEndMinutes) {
@@ -144,24 +139,15 @@ export function QuietHours() {
} else { } else {
// For one-time windows // For one-time windows
const startDate = new Date(record.start) const startDate = new Date(record.start)
const endDate = record.end ? new Date(record.end) : null const endDate = new Date(record.end)
if (endDate) { if (now >= startDate && now < endDate) {
if (now >= startDate && now <= endDate) {
return "active" return "active"
} else if (now > endDate) { } else if (now >= endDate) {
return "past" return "past"
} else { } else {
return "future" return "future"
} }
} else {
// No end date
if (now >= startDate) {
return "active"
} else {
return "future"
}
}
} }
} }
@@ -302,7 +288,7 @@ function QuietHoursDialog({
toast, toast,
}: { }: {
editingRecord: QuietHoursRecord | null editingRecord: QuietHoursRecord | null
systems: any[] systems: SystemRecord[]
onClose: () => void onClose: () => void
toast: any toast: any
}) { }) {
@@ -334,14 +320,20 @@ function QuietHoursDialog({
setEndDateTime(endDate ? formatDateTimeLocal(endDate) : "") setEndDateTime(endDate ? formatDateTimeLocal(endDate) : "")
} }
} else { } else {
// Reset form // Reset form with default dates: today at 12pm and 1pm
const today = new Date()
const noon = new Date(today)
noon.setHours(12, 0, 0, 0)
const onePm = new Date(today)
onePm.setHours(13, 0, 0, 0)
setSelectedSystem("") setSelectedSystem("")
setIsGlobal(true) setIsGlobal(true)
setWindowType("one-time") setWindowType("one-time")
setStartDateTime("") setStartDateTime(formatDateTimeLocal(noon))
setEndDateTime("") setEndDateTime(formatDateTimeLocal(onePm))
setStartTime("") setStartTime("12:00")
setEndTime("") setEndTime("13:00")
} }
}, [editingRecord]) }, [editingRecord])
@@ -484,11 +476,12 @@ function QuietHoursDialog({
onChange={(e) => setStartDateTime(e.target.value)} onChange={(e) => setStartDateTime(e.target.value)}
min={formatDateTimeLocal(new Date(new Date().setHours(0, 0, 0, 0)))} min={formatDateTimeLocal(new Date(new Date().setHours(0, 0, 0, 0)))}
required required
className="tabular-nums tracking-tighter"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="end-datetime"> <Label htmlFor="end-datetime">
<Trans>End Date & Time</Trans> (<Trans>Optional</Trans>) <Trans>End Date & Time</Trans>
</Label> </Label>
<Input <Input
id="end-datetime" id="end-datetime"
@@ -496,6 +489,8 @@ function QuietHoursDialog({
value={endDateTime} value={endDateTime}
onChange={(e) => setEndDateTime(e.target.value)} onChange={(e) => setEndDateTime(e.target.value)}
min={startDateTime || formatDateTimeLocal(new Date())} min={startDateTime || formatDateTimeLocal(new Date())}
required
className="tabular-nums tracking-tighter"
/> />
</div> </div>
</> </>
@@ -515,9 +510,9 @@ function QuietHoursDialog({
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="end-time"> <Label htmlFor="end-time">
<Trans>End Time</Trans> (<Trans>Optional</Trans>) <Trans>End Time</Trans>
</Label> </Label>
<Input id="end-time" type="time" value={endTime} onChange={(e) => setEndTime(e.target.value)} /> <Input id="end-time" type="time" value={endTime} onChange={(e) => setEndTime(e.target.value)} required />
</div> </div>
</> </>
)} )}

View File

@@ -250,7 +250,7 @@ export interface QuietHoursRecord extends RecordModel {
system: string system: string
type: "one-time" | "daily" type: "one-time" | "daily"
start: string start: string
end?: string | null end: string
expand?: { expand?: {
system?: { system?: {
name: string name: string