feat(monitor): proportion-based overall status + reusable auto-refresh
- Change overall status logic: >50% failed = UNAVAILABLE, any failed or degraded = DEGRADED, all ok = OPERATIONAL - Extract useAutoRefresh composable with localStorage persistence - Create AutoRefreshButton dropdown component (reusable) - Integrate auto-refresh into channel status page via MonitorHero
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { ref, onBeforeUnmount, type Ref } from 'vue'
|
||||
|
||||
export interface UseAutoRefreshOptions {
|
||||
storageKey: string
|
||||
intervals?: readonly number[]
|
||||
defaultInterval?: number
|
||||
onRefresh: () => Promise<void> | void
|
||||
/** Skip tick when this returns true (e.g. modal open, document hidden). */
|
||||
shouldPause?: () => boolean
|
||||
}
|
||||
|
||||
export function useAutoRefresh(options: UseAutoRefreshOptions) {
|
||||
const {
|
||||
storageKey,
|
||||
intervals = [5, 10, 15, 30] as const,
|
||||
defaultInterval,
|
||||
onRefresh,
|
||||
shouldPause,
|
||||
} = options
|
||||
|
||||
const enabled = ref(false)
|
||||
const intervalSeconds = ref(defaultInterval ?? intervals[intervals.length - 1])
|
||||
const countdown = ref(0)
|
||||
const fetching = ref(false)
|
||||
|
||||
let timerId: number | undefined
|
||||
|
||||
function loadFromStorage() {
|
||||
try {
|
||||
const saved = localStorage.getItem(storageKey)
|
||||
if (!saved) return
|
||||
const parsed = JSON.parse(saved) as { enabled?: boolean; interval_seconds?: number }
|
||||
enabled.value = parsed.enabled === true
|
||||
const iv = Number(parsed.interval_seconds)
|
||||
if (intervals.includes(iv as any)) intervalSeconds.value = iv
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
function saveToStorage() {
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify({
|
||||
enabled: enabled.value,
|
||||
interval_seconds: intervalSeconds.value,
|
||||
}))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
async function tick() {
|
||||
if (!enabled.value) return
|
||||
if (shouldPause?.()) return
|
||||
if (fetching.value) return
|
||||
|
||||
if (countdown.value <= 0) {
|
||||
countdown.value = intervalSeconds.value
|
||||
fetching.value = true
|
||||
try { await onRefresh() } finally { fetching.value = false }
|
||||
return
|
||||
}
|
||||
countdown.value -= 1
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (timerId !== undefined) return
|
||||
timerId = setInterval(tick, 1000) as unknown as number
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (timerId !== undefined) {
|
||||
clearInterval(timerId)
|
||||
timerId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
function setEnabled(value: boolean) {
|
||||
enabled.value = value
|
||||
saveToStorage()
|
||||
if (value) {
|
||||
countdown.value = intervalSeconds.value
|
||||
start()
|
||||
} else {
|
||||
stop()
|
||||
countdown.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
function setInterval_(seconds: number) {
|
||||
intervalSeconds.value = seconds
|
||||
saveToStorage()
|
||||
if (enabled.value) countdown.value = seconds
|
||||
}
|
||||
|
||||
function resetCountdown() {
|
||||
countdown.value = intervalSeconds.value
|
||||
}
|
||||
|
||||
loadFromStorage()
|
||||
|
||||
onBeforeUnmount(stop)
|
||||
|
||||
return {
|
||||
enabled: enabled as Ref<boolean>,
|
||||
intervalSeconds: intervalSeconds as Ref<number>,
|
||||
countdown: countdown as Ref<number>,
|
||||
fetching: fetching as Ref<boolean>,
|
||||
intervals,
|
||||
setEnabled,
|
||||
setInterval: setInterval_,
|
||||
resetCountdown,
|
||||
start,
|
||||
stop,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user