mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-24 14:36:17 +01:00
Compare commits
2 Commits
56807dc5e4
...
quiet-hour
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1275af956b | ||
|
|
bf36015bd9 |
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
2
internal/site/src/types.d.ts
vendored
2
internal/site/src/types.d.ts
vendored
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user