import { pb } from '@/lib/stores' import { alertInfo, cn } from '@/lib/utils' import { Switch } from '@/components/ui/switch' import { AlertRecord, SystemRecord } from '@/types' import { lazy, Suspense, useRef, useState } from 'react' import { toast } from '../ui/use-toast' import { RecordOptions } from 'pocketbase' import { newQueue, Queue } from '@henrygd/queue' interface AlertData { checked?: boolean val?: number min?: number updateAlert?: (checked: boolean, value: number, min: number) => void key: keyof typeof alertInfo alert: (typeof alertInfo)[keyof typeof alertInfo] system: SystemRecord } const Slider = lazy(() => import('@/components/ui/slider')) let queue: Queue const failedUpdateToast = () => toast({ title: 'Failed to update alert', description: 'Please check logs for more details.', variant: 'destructive', }) export function SystemAlert({ system, systemAlerts, data, }: { system: SystemRecord systemAlerts: AlertRecord[] data: AlertData }) { const alert = systemAlerts.find((alert) => alert.name === data.key) data.updateAlert = async (checked: boolean, value: number, min: number) => { try { if (alert && !checked) { await pb.collection('alerts').delete(alert.id) } else if (alert && checked) { await pb.collection('alerts').update(alert.id, { value, min, triggered: false }) } else if (checked) { pb.collection('alerts').create({ system: system.id, user: pb.authStore.model!.id, name: data.key, value: value, min: min, }) } } catch (e) { failedUpdateToast() } } if (alert) { data.checked = true data.val = alert.value data.min = alert.min || 1 } return } export function SystemAlertGlobal({ data, overwrite, alerts, systems, }: { data: AlertData overwrite: boolean | 'indeterminate' alerts: AlertRecord[] systems: SystemRecord[] }) { const systemsWithExistingAlerts = useRef<{ set: Set; populatedSet: boolean }>({ set: new Set(), populatedSet: false, }) data.checked = false data.val = data.min = 0 data.updateAlert = (checked: boolean, value: number, min: number) => { if (!queue) { queue = newQueue(5) } const { set, populatedSet } = systemsWithExistingAlerts.current // if overwrite checked, make sure all alerts will be overwritten if (overwrite) { set.clear() } const recordData: Partial = { value, min, triggered: false, } for (let system of systems) { // if overwrite is false and system is in set (alert existed), skip if (!overwrite && set.has(system)) { continue } // find matching existing alert const existingAlert = alerts.find( (alert) => alert.system === system.id && data.key === alert.name ) // if first run, add system to set (alert already existed when global panel was opened) if (existingAlert && !populatedSet && !overwrite) { set.add(system) continue } const requestOptions: RecordOptions = { requestKey: system.id, } // checked - make sure alert is created or updated if (checked) { if (existingAlert) { // console.log('updating', system.name) queue .add(() => pb.collection('alerts').update(existingAlert.id, recordData, requestOptions)) .catch(failedUpdateToast) } else { // console.log('creating', system.name) queue .add(() => pb.collection('alerts').create( { system: system.id, user: pb.authStore.model!.id, name: data.key, ...recordData, }, requestOptions ) ) .catch(failedUpdateToast) } } else if (existingAlert) { // console.log('deleting', system.name) queue.add(() => pb.collection('alerts').delete(existingAlert.id)).catch(failedUpdateToast) } } systemsWithExistingAlerts.current.populatedSet = true } return } function AlertContent({ data }: { data: AlertData }) { const { key } = data const hasSliders = !('single' in data.alert) const [checked, setChecked] = useState(data.checked || false) const [min, setMin] = useState(data.min || (hasSliders ? 10 : 0)) const [value, setValue] = useState(data.val || (hasSliders ? 80 : 0)) const showSliders = checked && hasSliders const newMin = useRef(min) const newValue = useRef(value) const Icon = alertInfo[key].icon const updateAlert = (c?: boolean) => data.updateAlert?.(c ?? checked, newValue.current, newMin.current) return (
{showSliders && (
}>

Average exceeds{' '} {value} {data.alert.unit}

(newValue.current = val[0]) && updateAlert()} onValueChange={(val) => setValue(val[0])} min={1} max={99} />

For {min} minute {min > 1 && 's'}

(newMin.current = val[0]) && updateAlert()} onValueChange={(val) => setMin(val[0])} min={1} max={60} />
)}
) }