From 739ad38e29404241659b1f54d179af87d66bb7cd Mon Sep 17 00:00:00 2001 From: Developer Date: Mon, 22 Jun 2026 19:13:59 +0000 Subject: [PATCH] style(services): apply formatter to frontend services runtime --- frontend/src/App.tsx | 10 +- frontend/src/api/services.ts | 4 +- frontend/src/api/widgets.ts | 4 +- .../src/components/WidgetConfigDialog.tsx | 134 +++++++++++++----- frontend/src/integrations/registry.test.ts | 4 +- frontend/src/integrations/registry.ts | 17 ++- frontend/src/pages/ServicePage.tsx | 29 ++-- frontend/src/widgets/BackupsWidget.tsx | 6 +- frontend/src/widgets/GrafanaLinkWidget.tsx | 6 +- frontend/src/widgets/JellyfinWidget.tsx | 6 +- .../src/widgets/PrometheusMetricWidget.tsx | 6 +- frontend/src/widgets/SshTaskWidget.tsx | 6 +- frontend/src/widgets/StaticWidget.tsx | 6 +- 13 files changed, 177 insertions(+), 61 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 570c775..1f285dc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -452,7 +452,10 @@ function AppInner() { } /> } /> } /> - } /> + } + /> @@ -485,7 +488,10 @@ function AppInner() { } /> } /> } /> - } /> + } + /> diff --git a/frontend/src/api/services.ts b/frontend/src/api/services.ts index e011076..7322e42 100644 --- a/frontend/src/api/services.ts +++ b/frontend/src/api/services.ts @@ -15,7 +15,9 @@ export async function fetchServiceTypes(): Promise { export async function fetchServiceInstances( serviceType?: string, ): Promise { - const query = serviceType ? `?service_type=${encodeURIComponent(serviceType)}` : ""; + const query = serviceType + ? `?service_type=${encodeURIComponent(serviceType)}` + : ""; const res = await fetch(`${API_BASE}/services/instances${query}`); if (!res.ok) throw new Error("Failed to fetch service instances"); return res.json(); diff --git a/frontend/src/api/widgets.ts b/frontend/src/api/widgets.ts index bed9574..c395f84 100644 --- a/frontend/src/api/widgets.ts +++ b/frontend/src/api/widgets.ts @@ -7,7 +7,9 @@ import type { const API_BASE = "/api"; -export async function fetchBuiltinWidgetKinds(): Promise { +export async function fetchBuiltinWidgetKinds(): Promise< + BuiltinWidgetKindInfo[] +> { const res = await fetch(`${API_BASE}/widgets/builtin`); if (!res.ok) throw new Error("Failed to fetch built-in widget kinds"); return res.json(); diff --git a/frontend/src/components/WidgetConfigDialog.tsx b/frontend/src/components/WidgetConfigDialog.tsx index 4d75426..f898b30 100644 --- a/frontend/src/components/WidgetConfigDialog.tsx +++ b/frontend/src/components/WidgetConfigDialog.tsx @@ -71,7 +71,8 @@ function Field({ } function bindingLabel(serviceId: string | null, widgetKind: string): string { - if (serviceId === null) return BUILTIN_WIDGETS[widgetKind]?.name ?? widgetKind; + if (serviceId === null) + return BUILTIN_WIDGETS[widgetKind]?.name ?? widgetKind; return widgetKind; } @@ -116,8 +117,11 @@ function WidgetConfigEditor({ const properties = binding ? Object.entries( - (binding.configSchema as { properties?: Record } | undefined) - ?.properties ?? {}, + ( + binding.configSchema as + | { properties?: Record } + | undefined + )?.properties ?? {}, ) : []; @@ -188,8 +192,9 @@ export function WidgetConfigDialog({ open, onClose }: Props) { } function startAddService(serviceId: string, kind: string) { - const binding = SERVICE_REGISTRY[services.find((s) => s.id === serviceId)?.service_type ?? ""] - ?.widgets.find((w) => w.kind === kind); + const binding = SERVICE_REGISTRY[ + services.find((s) => s.id === serviceId)?.service_type ?? "" + ]?.widgets.find((w) => w.kind === kind); setDraft({ serviceId, widgetKind: kind, @@ -267,19 +272,27 @@ export function WidgetConfigDialog({ open, onClose }: Props) { const draftBinding = draft ? draft.serviceId - ? SERVICE_REGISTRY[services.find((s) => s.id === draft.serviceId)?.service_type ?? ""] - ?.widgets.find((w) => w.kind === draft.widgetKind) + ? SERVICE_REGISTRY[ + services.find((s) => s.id === draft.serviceId)?.service_type ?? "" + ]?.widgets.find((w) => w.kind === draft.widgetKind) : BUILTIN_WIDGETS[draft.widgetKind] : undefined; const isTaskOutput = draft?.serviceId !== null && - services.find((s) => s.id === draft?.serviceId)?.service_type === "ssh_tasks"; + services.find((s) => s.id === draft?.serviceId)?.service_type === + "ssh_tasks"; return ( - {draft ? (draft.id ? "Edit widget" : "Add widget") : "Dashboard widgets"} + + {draft + ? draft.id + ? "Edit widget" + : "Add widget" + : "Dashboard widgets"} + {draft ? ( @@ -289,7 +302,9 @@ export function WidgetConfigDialog({ open, onClose }: Props) { setDraft({ ...draft, title: e.target.value })} + onChange={(e) => + setDraft({ ...draft, title: e.target.value }) + } /> @@ -300,7 +315,8 @@ export function WidgetConfigDialog({ open, onClose }: Props) { onChange={(e) => setDraft({ ...draft, - sortOrder: e.target.value === "" ? 0 : Number(e.target.value), + sortOrder: + e.target.value === "" ? 0 : Number(e.target.value), }) } /> @@ -310,7 +326,9 @@ export function WidgetConfigDialog({ open, onClose }: Props) { setDraft({ ...draft, enabled: checked })} + onCheckedChange={(checked) => + setDraft({ ...draft, enabled: checked }) + } /> @@ -334,7 +352,9 @@ export function WidgetConfigDialog({ open, onClose }: Props) {
{sortedInstances.length === 0 ? ( - No widgets yet. Add one below. + + No widgets yet. Add one below. + ) : (
@@ -343,31 +363,67 @@ export function WidgetConfigDialog({ open, onClose }: Props) { ? services.find((s) => s.id === instance.service_id)?.name : "Built-in"; return ( -
+
{instance.title} - {bindingLabel(instance.service_id, instance.widget_kind)} + {bindingLabel( + instance.service_id, + instance.widget_kind, + )} {serviceName ? ( - {serviceName} + + {serviceName} + + ) : null} + {!instance.enabled ? ( + disabled ) : null} - {!instance.enabled ? disabled : null}
- - - toggleEnabled(instance)} aria-label={`Toggle ${instance.title}`} /> - -
@@ -381,7 +437,12 @@ export function WidgetConfigDialog({ open, onClose }: Props) {

Add widget

{Object.values(BUILTIN_WIDGETS).map((b) => ( - @@ -389,21 +450,24 @@ export function WidgetConfigDialog({ open, onClose }: Props) { {services .filter((s) => s.enabled) .flatMap((s) => - (SERVICE_REGISTRY[s.service_type]?.widgets ?? []).map((w) => ( - - )), + (SERVICE_REGISTRY[s.service_type]?.widgets ?? []).map( + (w) => ( + + ), + ), )}

- Configure services on their service pages to unlock more widgets. + Configure services on their service pages to unlock more + widgets.

diff --git a/frontend/src/integrations/registry.test.ts b/frontend/src/integrations/registry.test.ts index 9026191..376e99c 100644 --- a/frontend/src/integrations/registry.test.ts +++ b/frontend/src/integrations/registry.test.ts @@ -20,7 +20,9 @@ describe("service registry", () => { }); it("binds widget kinds per service", () => { - expect(SERVICE_REGISTRY.grafana.widgets.map((w) => w.kind)).toEqual(["link"]); + expect(SERVICE_REGISTRY.grafana.widgets.map((w) => w.kind)).toEqual([ + "link", + ]); expect(SERVICE_REGISTRY.ssh_tasks.widgets.map((w) => w.kind)).toEqual([ "task_output", ]); diff --git a/frontend/src/integrations/registry.ts b/frontend/src/integrations/registry.ts index f7113ef..16c87d3 100644 --- a/frontend/src/integrations/registry.ts +++ b/frontend/src/integrations/registry.ts @@ -55,7 +55,10 @@ export const SERVICE_REGISTRY: Record = { defaultConfig: { dashboard_uid: "" }, configSchema: { type: "object", - properties: { dashboard_uid: { type: "string" }, panel_id: { type: "integer" } }, + properties: { + dashboard_uid: { type: "string" }, + panel_id: { type: "integer" }, + }, required: ["dashboard_uid"], }, component: GrafanaLinkWidget, @@ -151,11 +154,15 @@ export const BUILTIN_WIDGETS: Record = { }, }; -export function getServiceBinding(serviceType: string): ServiceBinding | undefined { +export function getServiceBinding( + serviceType: string, +): ServiceBinding | undefined { return SERVICE_REGISTRY[serviceType]; } -export function getBuiltinBinding(kind: string): ServiceWidgetBinding | undefined { +export function getBuiltinBinding( + kind: string, +): ServiceWidgetBinding | undefined { return BUILTIN_WIDGETS[kind]; } @@ -199,6 +206,8 @@ export function resolveWidget( } /** Merge backend type metadata (config_schema, secret_fields) onto bindings. */ -export function enrichServiceTypes(types: ServiceTypeInfo[]): ServiceTypeInfo[] { +export function enrichServiceTypes( + types: ServiceTypeInfo[], +): ServiceTypeInfo[] { return types; } diff --git a/frontend/src/pages/ServicePage.tsx b/frontend/src/pages/ServicePage.tsx index 880323b..ac5012e 100644 --- a/frontend/src/pages/ServicePage.tsx +++ b/frontend/src/pages/ServicePage.tsx @@ -31,7 +31,9 @@ function Field({
{children} - {helper ?

{helper}

: null} + {helper ? ( +

{helper}

+ ) : null}
); } @@ -66,9 +68,7 @@ export function ServicePage() { if (!binding) { return ( - - Unknown service type: {serviceType} - + Unknown service type: {serviceType} ); } @@ -127,10 +127,7 @@ export function ServicePage() { -
@@ -140,7 +137,10 @@ export function ServicePage() { {binding.widgets.length > 0 ? ( - +
{binding.widgets.map((w) => (
{Object.entries(instance.secrets_set).map(([key, isSet]) => (
- + - setDraftSecrets({ ...draftSecrets, [key]: e.target.value }) + setDraftSecrets({ + ...draftSecrets, + [key]: e.target.value, + }) } /> diff --git a/frontend/src/widgets/BackupsWidget.tsx b/frontend/src/widgets/BackupsWidget.tsx index f577bc1..74f2b0d 100644 --- a/frontend/src/widgets/BackupsWidget.tsx +++ b/frontend/src/widgets/BackupsWidget.tsx @@ -12,7 +12,11 @@ interface Props { description?: string; } -export function BackupsWidget({ widget, refreshIntervalMs, description }: Props) { +export function BackupsWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs); const summary = data?.data as BackupDashboardSummary | undefined; diff --git a/frontend/src/widgets/GrafanaLinkWidget.tsx b/frontend/src/widgets/GrafanaLinkWidget.tsx index 8001534..5840fd8 100644 --- a/frontend/src/widgets/GrafanaLinkWidget.tsx +++ b/frontend/src/widgets/GrafanaLinkWidget.tsx @@ -12,7 +12,11 @@ interface Props { description?: string; } -export function GrafanaLinkWidget({ widget, refreshIntervalMs, description }: Props) { +export function GrafanaLinkWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs); const url = data?.data?.url as string | undefined; diff --git a/frontend/src/widgets/JellyfinWidget.tsx b/frontend/src/widgets/JellyfinWidget.tsx index 127fed0..68aaf72 100644 --- a/frontend/src/widgets/JellyfinWidget.tsx +++ b/frontend/src/widgets/JellyfinWidget.tsx @@ -11,7 +11,11 @@ interface Props { description?: string; } -export function JellyfinWidget({ widget, refreshIntervalMs, description }: Props) { +export function JellyfinWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs); const sessions = data?.data?.sessions as NowPlayingSession[] | undefined; diff --git a/frontend/src/widgets/PrometheusMetricWidget.tsx b/frontend/src/widgets/PrometheusMetricWidget.tsx index bc4862c..dc617a2 100644 --- a/frontend/src/widgets/PrometheusMetricWidget.tsx +++ b/frontend/src/widgets/PrometheusMetricWidget.tsx @@ -36,7 +36,11 @@ function formatPrometheusValue(result: PromQLResult | undefined): string { return JSON.stringify(result, null, 2); } -export function PrometheusMetricWidget({ widget, refreshIntervalMs, description }: Props) { +export function PrometheusMetricWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs); const result = data?.data?.result as PromQLResult | undefined; diff --git a/frontend/src/widgets/SshTaskWidget.tsx b/frontend/src/widgets/SshTaskWidget.tsx index 6013682..c51b037 100644 --- a/frontend/src/widgets/SshTaskWidget.tsx +++ b/frontend/src/widgets/SshTaskWidget.tsx @@ -16,7 +16,11 @@ type SshTaskResult = { stderr: string; }; -export function SshTaskWidget({ widget, refreshIntervalMs, description }: Props) { +export function SshTaskWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs); const result = data?.data as SshTaskResult | undefined; diff --git a/frontend/src/widgets/StaticWidget.tsx b/frontend/src/widgets/StaticWidget.tsx index 3edcc80..ba876b5 100644 --- a/frontend/src/widgets/StaticWidget.tsx +++ b/frontend/src/widgets/StaticWidget.tsx @@ -8,7 +8,11 @@ interface Props { description?: string; } -export function StaticWidget({ widget, refreshIntervalMs, description }: Props) { +export function StaticWidget({ + widget, + refreshIntervalMs, + description, +}: Props) { const { data } = useWidgetData(widget.id, refreshIntervalMs); const text = data?.data?.text as string | undefined;