e1356b20f1
PR 3 of 4 for configurable dashboard widgets. - Add TypeScript widget interfaces (WidgetInstance, WidgetInstanceInput, WidgetTypeInfo, WidgetDataResponse). - Create widget API client for CRUD, registry metadata, and per-widget data. - Create TanStack Query hooks for instances, data, sources, types, and mutations. - Create closed frontend widget registry with metadata, source type, refresh intervals, and config fields. - Add six shadcn/ui-based widget components: Jellyfin, Backups, Grafana link, Prometheus metric, SSH task output, and static text. - Add Vitest unit tests for registry metadata. Verification: - backend ruff clean; pytest 200 passed - frontend npm run lint: 0 errors - frontend npm run build: success - frontend npm run test -- src/widgets/registry.test.ts: 3 passed
70 lines
2.1 KiB
TypeScript
70 lines
2.1 KiB
TypeScript
import type {
|
|
WidgetDataResponse,
|
|
WidgetInstance,
|
|
WidgetInstanceInput,
|
|
WidgetTypeInfo,
|
|
} from "../types";
|
|
|
|
const API_BASE = "/api";
|
|
|
|
export async function fetchWidgetSources(): Promise<string[]> {
|
|
const res = await fetch(`${API_BASE}/widgets/sources`);
|
|
if (!res.ok) throw new Error("Failed to fetch widget sources");
|
|
return res.json();
|
|
}
|
|
|
|
export async function fetchWidgetTypes(): Promise<WidgetTypeInfo[]> {
|
|
const res = await fetch(`${API_BASE}/widgets/types`);
|
|
if (!res.ok) throw new Error("Failed to fetch widget types");
|
|
return res.json();
|
|
}
|
|
|
|
export async function fetchWidgetInstances(): Promise<WidgetInstance[]> {
|
|
const res = await fetch(`${API_BASE}/widgets/instances`);
|
|
if (!res.ok) throw new Error("Failed to fetch widget instances");
|
|
return res.json();
|
|
}
|
|
|
|
export async function createWidgetInstance(
|
|
input: WidgetInstanceInput,
|
|
): Promise<WidgetInstance> {
|
|
const res = await fetch(`${API_BASE}/widgets/instances`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(input),
|
|
});
|
|
if (!res.ok) throw new Error("Failed to create widget instance");
|
|
return res.json();
|
|
}
|
|
|
|
export async function updateWidgetInstance(
|
|
input: WidgetInstanceInput,
|
|
): Promise<WidgetInstance> {
|
|
if (!input.id) throw new Error("Widget ID is required for update");
|
|
const res = await fetch(`${API_BASE}/widgets/instances/${input.id}`, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(input),
|
|
});
|
|
if (!res.ok) throw new Error("Failed to update widget instance");
|
|
return res.json();
|
|
}
|
|
|
|
export async function deleteWidgetInstance(
|
|
widgetId: string,
|
|
): Promise<{ status: string }> {
|
|
const res = await fetch(`${API_BASE}/widgets/instances/${widgetId}`, {
|
|
method: "DELETE",
|
|
});
|
|
if (!res.ok) throw new Error("Failed to delete widget instance");
|
|
return res.json();
|
|
}
|
|
|
|
export async function fetchWidgetData(
|
|
widgetId: string,
|
|
): Promise<WidgetDataResponse> {
|
|
const res = await fetch(`${API_BASE}/widgets/instances/${widgetId}/data`);
|
|
if (!res.ok) throw new Error("Failed to fetch widget data");
|
|
return res.json();
|
|
}
|