1e636fdbe2
Three reusable-widget follow-up fixes:
1. Reference sort_order independently reorderable. Reordering a
referenced widget now updates the widget_references.sort_order (per-
dashboard), not the shared widget instance sort_order. New backend
update_widget_reference method + PUT /api/widgets/references/{id}
endpoint. Frontend moveInstance checks _ref_id to choose the right
mutation (updateRef for references, saveWidget for owned).
2. Detach preserves service_id. detach_widget_reference now copies the
original widget's service_id into the clone, so service-bound widgets
(Grafana chart, Jellyfin activity) continue to render after detach.
3. Named dashboards support widget references. NamedDashboardPage
fetches useWidgetReferences('named:<slug>') and renders them via
WidgetInstanceCard alongside pinned links. 'Edit widgets' button
opens WidgetConfigDialog with dashboardScope='named:<slug>'.
Also: removed useMemo on combinedWidgets in WidgetConfigDialog to fix
a react-hooks/preserve-manual-memoization lint error (the React Compiler
ESLint plugin couldn't verify the spread+sort memoization).
283 backend tests pass (+1 update_reference test); 128 frontend tests
pass; ruff clean; 0 lint errors.
100 lines
2.6 KiB
TypeScript
100 lines
2.6 KiB
TypeScript
import { del, get, post, put } from "./shared";
|
|
import type {
|
|
BuiltinWidgetKindInfo,
|
|
WidgetDataResponse,
|
|
WidgetInstance,
|
|
WidgetInstanceInput,
|
|
} from "../types";
|
|
|
|
export interface WidgetReference {
|
|
id: string;
|
|
dashboard_scope: string;
|
|
widget_id: string;
|
|
sort_order: number;
|
|
created_at: number;
|
|
widget: WidgetInstance;
|
|
}
|
|
|
|
export interface WidgetReferenceInput {
|
|
dashboard_scope: string;
|
|
widget_id: string;
|
|
sort_order?: number;
|
|
}
|
|
|
|
export async function fetchBuiltinWidgetKinds(): Promise<
|
|
BuiltinWidgetKindInfo[]
|
|
> {
|
|
return get<BuiltinWidgetKindInfo[]>("/api/widgets/builtin");
|
|
}
|
|
|
|
export async function fetchWidgetInstances(
|
|
serviceId?: string,
|
|
scope?: "dashboard" | "service",
|
|
): Promise<WidgetInstance[]> {
|
|
const params: Record<string, string> = {};
|
|
if (serviceId) params.service_id = serviceId;
|
|
if (scope) params.scope = scope;
|
|
return get<WidgetInstance[]>("/api/widgets/instances", params);
|
|
}
|
|
|
|
export async function createWidgetInstance(
|
|
input: WidgetInstanceInput,
|
|
): Promise<WidgetInstance> {
|
|
return post<WidgetInstance>("/api/widgets/instances", input);
|
|
}
|
|
|
|
export async function updateWidgetInstance(
|
|
input: WidgetInstanceInput,
|
|
): Promise<WidgetInstance> {
|
|
if (!input.id) throw new Error("Widget ID is required for update");
|
|
return put<WidgetInstance>(`/api/widgets/instances/${input.id}`, input);
|
|
}
|
|
|
|
export async function deleteWidgetInstance(
|
|
widgetId: string,
|
|
): Promise<{ status: string }> {
|
|
return del<{ status: string }>(`/api/widgets/instances/${widgetId}`);
|
|
}
|
|
|
|
export async function fetchWidgetData(
|
|
widgetId: string,
|
|
): Promise<WidgetDataResponse> {
|
|
return get<WidgetDataResponse>(`/api/widgets/instances/${widgetId}/data`);
|
|
}
|
|
|
|
export async function fetchWidgetReferences(
|
|
dashboardScope: string,
|
|
): Promise<WidgetReference[]> {
|
|
return get<WidgetReference[]>("/api/widgets/references", {
|
|
dashboard_scope: dashboardScope,
|
|
});
|
|
}
|
|
|
|
export async function createWidgetReference(
|
|
input: WidgetReferenceInput,
|
|
): Promise<WidgetReference> {
|
|
return post<WidgetReference>("/api/widgets/references", input);
|
|
}
|
|
|
|
export async function deleteWidgetReference(
|
|
referenceId: string,
|
|
): Promise<{ status: string }> {
|
|
return del<{ status: string }>(`/api/widgets/references/${referenceId}`);
|
|
}
|
|
|
|
export async function detachWidgetReference(
|
|
referenceId: string,
|
|
): Promise<WidgetInstance> {
|
|
return post<WidgetInstance>(`/api/widgets/references/${referenceId}/detach`);
|
|
}
|
|
|
|
export async function updateWidgetReference(
|
|
referenceId: string,
|
|
sortOrder: number,
|
|
): Promise<WidgetReference> {
|
|
return put<WidgetReference>(
|
|
`/api/widgets/references/${referenceId}?sort_order=${sortOrder}`,
|
|
{},
|
|
);
|
|
}
|