Skip to content

Commit 191f25f

Browse files
committed
ui: refactor heartbeat settings page
1 parent aa8b371 commit 191f25f

File tree

31 files changed

+106
-122
lines changed

31 files changed

+106
-122
lines changed

internal/site/src/components/routes/settings/heartbeat.tsx

Lines changed: 106 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { t } from "@lingui/core/macro"
22
import { Trans } from "@lingui/react/macro"
33
import { redirectPage } from "@nanostores/router"
4-
import clsx from "clsx"
54
import { LoaderCircleIcon, SendIcon } from "lucide-react"
65
import { useEffect, useState } from "react"
76
import { $router } from "@/components/router"
@@ -10,6 +9,7 @@ import { Button } from "@/components/ui/button"
109
import { Separator } from "@/components/ui/separator"
1110
import { toast } from "@/components/ui/use-toast"
1211
import { isAdmin, pb } from "@/lib/api"
12+
import { cn } from "@/lib/utils"
1313

1414
interface HeartbeatStatus {
1515
enabled: boolean
@@ -37,10 +37,10 @@ export default function HeartbeatSettings() {
3737
setIsLoading(true)
3838
const res = await pb.send<HeartbeatStatus>("/api/beszel/heartbeat-status", {})
3939
setStatus(res)
40-
} catch (error: any) {
40+
} catch (error: unknown) {
4141
toast({
4242
title: t`Error`,
43-
description: error.message,
43+
description: (error as Error).message,
4444
variant: "destructive",
4545
})
4646
} finally {
@@ -66,19 +66,17 @@ export default function HeartbeatSettings() {
6666
variant: "destructive",
6767
})
6868
}
69-
} catch (error: any) {
69+
} catch (error: unknown) {
7070
toast({
7171
title: t`Error`,
72-
description: error.message,
72+
description: (error as Error).message,
7373
variant: "destructive",
7474
})
7575
} finally {
7676
setIsTesting(false)
7777
}
7878
}
7979

80-
const TestIcon = isTesting ? LoaderCircleIcon : SendIcon
81-
8280
return (
8381
<div>
8482
<div>
@@ -94,107 +92,123 @@ export default function HeartbeatSettings() {
9492
</div>
9593
<Separator className="my-4" />
9694

97-
{isLoading ? (
98-
<div className="flex items-center gap-2 text-muted-foreground py-4">
99-
<LoaderCircleIcon className="h-4 w-4 animate-spin" />
100-
<Trans>Loading...</Trans>
101-
</div>
102-
) : status?.enabled ? (
103-
<div className="space-y-5">
104-
<div className="flex items-center gap-2">
105-
<Badge variant="success">
106-
<Trans>Active</Trans>
107-
</Badge>
108-
</div>
109-
<div className="grid gap-4 sm:grid-cols-2">
110-
<ConfigItem label={t`Endpoint URL`} value={status.url ?? ""} mono />
111-
<ConfigItem label={t`Interval`} value={`${status.interval}s`} />
112-
<ConfigItem label={t`HTTP Method`} value={status.method ?? "POST"} />
113-
</div>
114-
115-
<Separator />
116-
117-
<div>
118-
<h4 className="text-base font-medium mb-1">
119-
<Trans>Test heartbeat</Trans>
120-
</h4>
121-
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
122-
<Trans>Send a single heartbeat ping to verify your endpoint is working.</Trans>
123-
</p>
124-
<Button
125-
type="button"
126-
variant="outline"
127-
className="flex items-center gap-1.5"
128-
onClick={sendTestHeartbeat}
129-
disabled={isTesting}
130-
>
131-
<TestIcon className={clsx("h-4 w-4", isTesting && "animate-spin")} />
132-
<Trans>Send test heartbeat</Trans>
133-
</Button>
134-
</div>
135-
136-
<Separator />
137-
138-
<div>
139-
<h4 className="text-base font-medium mb-2">
140-
<Trans>Payload format</Trans>
141-
</h4>
142-
<p className="text-sm text-muted-foreground leading-relaxed mb-2">
143-
<Trans>
144-
When using POST, each heartbeat includes a JSON payload with system status summary, list of down
145-
systems, and triggered alerts.
146-
</Trans>
147-
</p>
148-
<p className="text-sm text-muted-foreground leading-relaxed">
149-
<Trans>
150-
The overall status is <code className="bg-muted rounded-sm px-1 text-primary">ok</code> when all systems
151-
are up, <code className="bg-muted rounded-sm px-1 text-primary">warn</code> when alerts are triggered,
152-
and <code className="bg-muted rounded-sm px-1 text-primary">error</code> when any system is down.
153-
</Trans>
154-
</p>
155-
</div>
156-
</div>
95+
{status?.enabled ? (
96+
<EnabledState status={status} isTesting={isTesting} sendTestHeartbeat={sendTestHeartbeat} />
15797
) : (
158-
<div className="grid gap-4">
159-
<div>
160-
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
161-
<Trans>Set the following environment variables on your Beszel hub to enable heartbeat monitoring:</Trans>
162-
</p>
163-
<div className="grid gap-2.5">
164-
<EnvVarItem
165-
name="HEARTBEAT_URL"
166-
description={t`Endpoint URL to ping (required)`}
167-
example="https://uptime.betterstack.com/api/v1/heartbeat/xxxx"
168-
/>
169-
<EnvVarItem name="HEARTBEAT_INTERVAL" description={t`Seconds between pings (default: 60)`} example="60" />
170-
<EnvVarItem
171-
name="HEARTBEAT_METHOD"
172-
description={t`HTTP method: POST, GET, or HEAD (default: POST)`}
173-
example="POST"
174-
/>
175-
</div>
176-
</div>
177-
<p className="text-sm text-muted-foreground leading-relaxed">
178-
<Trans>After setting the environment variables, restart your Beszel hub for changes to take effect.</Trans>
179-
</p>
180-
</div>
98+
<NotEnabledState isLoading={isLoading} />
18199
)}
182100
</div>
183101
)
184102
}
185103

104+
function EnabledState({
105+
status,
106+
isTesting,
107+
sendTestHeartbeat,
108+
}: {
109+
status: HeartbeatStatus
110+
isTesting: boolean
111+
sendTestHeartbeat: () => void
112+
}) {
113+
const TestIcon = isTesting ? LoaderCircleIcon : SendIcon
114+
return (
115+
<div className="space-y-5">
116+
<div className="flex items-center gap-2">
117+
<Badge variant="success">
118+
<Trans>Active</Trans>
119+
</Badge>
120+
</div>
121+
<div className="grid gap-4 sm:grid-cols-2">
122+
<ConfigItem label={t`Endpoint URL`} value={status.url ?? ""} mono />
123+
<ConfigItem label={t`Interval`} value={`${status.interval}s`} />
124+
<ConfigItem label={t`HTTP Method`} value={status.method ?? "POST"} />
125+
</div>
126+
127+
<Separator />
128+
129+
<div>
130+
<h4 className="text-base font-medium mb-1">
131+
<Trans>Test heartbeat</Trans>
132+
</h4>
133+
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
134+
<Trans>Send a single heartbeat ping to verify your endpoint is working.</Trans>
135+
</p>
136+
<Button
137+
type="button"
138+
variant="outline"
139+
className="flex items-center gap-1.5"
140+
onClick={sendTestHeartbeat}
141+
disabled={isTesting}
142+
>
143+
<TestIcon className={cn("size-4", isTesting && "animate-spin")} />
144+
<Trans>Send test heartbeat</Trans>
145+
</Button>
146+
</div>
147+
148+
<Separator />
149+
150+
<div>
151+
<h4 className="text-base font-medium mb-2">
152+
<Trans>Payload format</Trans>
153+
</h4>
154+
<p className="text-sm text-muted-foreground leading-relaxed mb-2">
155+
<Trans>
156+
When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems,
157+
and triggered alerts.
158+
</Trans>
159+
</p>
160+
<p className="text-sm text-muted-foreground leading-relaxed">
161+
<Trans>
162+
The overall status is <code className="bg-muted rounded-sm px-1 text-primary">ok</code> when all systems are
163+
up, <code className="bg-muted rounded-sm px-1 text-primary">warn</code> when alerts are triggered, and{" "}
164+
<code className="bg-muted rounded-sm px-1 text-primary">error</code> when any system is down.
165+
</Trans>
166+
</p>
167+
</div>
168+
</div>
169+
)
170+
}
171+
172+
function NotEnabledState({ isLoading }: { isLoading?: boolean }) {
173+
return (
174+
<div className={cn("grid gap-4", isLoading && "animate-pulse")}>
175+
<div>
176+
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
177+
<Trans>Set the following environment variables on your Beszel hub to enable heartbeat monitoring:</Trans>
178+
</p>
179+
<div className="grid gap-2.5">
180+
<EnvVarItem
181+
name="HEARTBEAT_URL"
182+
description={t`Endpoint URL to ping (required)`}
183+
example="https://uptime.betterstack.com/api/v1/heartbeat/xxxx"
184+
/>
185+
<EnvVarItem name="HEARTBEAT_INTERVAL" description={t`Seconds between pings (default: 60)`} example="60" />
186+
<EnvVarItem
187+
name="HEARTBEAT_METHOD"
188+
description={t`HTTP method: POST, GET, or HEAD (default: POST)`}
189+
example="POST"
190+
/>
191+
</div>
192+
</div>
193+
<p className="text-sm text-muted-foreground leading-relaxed">
194+
<Trans>After setting the environment variables, restart your Beszel hub for changes to take effect.</Trans>
195+
</p>
196+
</div>
197+
)
198+
}
199+
186200
function ConfigItem({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
187201
return (
188202
<div>
189203
<p className="text-sm font-medium mb-0.5">{label}</p>
190-
<p className={clsx("text-sm text-muted-foreground break-all", mono && "font-mono")}>{value}</p>
204+
<p className={cn("text-sm text-muted-foreground break-all", mono && "font-mono")}>{value}</p>
191205
</div>
192206
)
193207
}
194208

195209
function EnvVarItem({ name, description, example }: { name: string; description: string; example: string }) {
196210
return (
197-
<div className="bg-muted/50 rounded-md px-3 py-2 grid gap-1.5">
211+
<div className="bg-muted/50 rounded-md px-3 py-2.5 grid gap-1.5">
198212
<code className="text-sm font-mono text-primary font-medium leading-tight">{name}</code>
199213
<p className="text-sm text-muted-foreground">{description}</p>
200214
<p className="text-xs text-muted-foreground">

internal/site/src/locales/ar/ar.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "متوسط التحميل"
937937
msgid "Load state"
938938
msgstr "حالة التحميل"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "جاري التحميل..."

internal/site/src/locales/bg/bg.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Средно натоварване"
937937
msgid "Load state"
938938
msgstr "Състояние на зареждане"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Зареждане..."

internal/site/src/locales/cs/cs.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Prům. zatížení"
937937
msgid "Load state"
938938
msgstr "Stav načtení"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Načítání..."

internal/site/src/locales/da/da.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Belastning gns."
937937
msgid "Load state"
938938
msgstr "Indlæsningstilstand"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Indlæser..."

internal/site/src/locales/de/de.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Systemlast"
937937
msgid "Load state"
938938
msgstr "Ladezustand"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Lädt..."

internal/site/src/locales/en/en.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,6 @@ msgstr "Load Avg"
932932
msgid "Load state"
933933
msgstr "Load state"
934934

935-
#: src/components/routes/settings/heartbeat.tsx
936935
#: src/components/systemd-table/systemd-table.tsx
937936
msgid "Loading..."
938937
msgstr "Loading..."

internal/site/src/locales/es/es.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Carga media"
937937
msgid "Load state"
938938
msgstr "Estado de carga"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Cargando..."

internal/site/src/locales/fa/fa.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "میانگین بار"
937937
msgid "Load state"
938938
msgstr "وضعیت بارگذاری"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "در حال بارگذاری..."

internal/site/src/locales/fr/fr.po

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ msgstr "Charge moy."
937937
msgid "Load state"
938938
msgstr "État de charge"
939939

940-
#: src/components/routes/settings/heartbeat.tsx
941940
#: src/components/systemd-table/systemd-table.tsx
942941
msgid "Loading..."
943942
msgstr "Chargement..."

0 commit comments

Comments
 (0)