Fix: settings master/detail, widget kind filter, reorder, media worker
Four fixes: 1. Settings > Services tab: master/detail layout. Replaced the vertical stack of ServiceConfigEditor cards with a SelectionRailCard (list on left) + SectionCard (details on right) — same pattern as Machines and SSH Keys tabs. Click a service in the rail to edit it. 2. Service Overview widget restriction. When adding widgets on a service's Overview, the dialog now only shows built-in widgets + widgets for THAT service type (not all services). Dashboard/named dashboards (no serviceId) still see all. 3. Reorder buttons fixed. The swap-sort_order approach was a no-op when both items had sort_order=0 (the default). Now moveInstance renumbers ALL items by their new index position (i * 10) after the swap, guaranteeing values change. References use updateRef, owned widgets use saveWidget, both sequential. 4. Media index worker resolution. The subprocess worker called get_jellyfin_client/get_user_id (FastAPI request dependencies) which don't work outside request context. Now accepts a service_id parameter (passed from post_build_index) and resolves the Jellyfin instance directly from the settings store via _resolve_jellyfin. Raises RuntimeError (not HTTPException) on failure. 283 backend tests pass; 128 frontend tests pass; ruff/eslint clean.
This commit is contained in:
@@ -232,7 +232,6 @@ export function WidgetConfigDialog({
|
||||
startEdit(target);
|
||||
}
|
||||
}
|
||||
|
||||
}, [open, editWidgetId, instances]);
|
||||
|
||||
function startAddBuiltIn(kind: string) {
|
||||
@@ -307,27 +306,26 @@ export function WidgetConfigDialog({
|
||||
async function moveInstance(index: number, direction: -1 | 1) {
|
||||
const targetIndex = index + direction;
|
||||
if (targetIndex < 0 || targetIndex >= combinedWidgets.length) return;
|
||||
const a = combinedWidgets[index];
|
||||
const b = combinedWidgets[targetIndex];
|
||||
const aRefId = (a as { _ref_id?: string })._ref_id;
|
||||
const bRefId = (b as { _ref_id?: string })._ref_id;
|
||||
// References use their own sort_order on the widget_references row;
|
||||
// owned widgets use the widget instance's sort_order.
|
||||
if (aRefId) {
|
||||
await updateRef.mutateAsync({
|
||||
referenceId: aRefId,
|
||||
sortOrder: b.sort_order,
|
||||
});
|
||||
} else {
|
||||
await saveWidget.mutateAsync({ ...a, sort_order: b.sort_order });
|
||||
}
|
||||
if (bRefId) {
|
||||
await updateRef.mutateAsync({
|
||||
referenceId: bRefId,
|
||||
sortOrder: a.sort_order,
|
||||
});
|
||||
} else {
|
||||
await saveWidget.mutateAsync({ ...b, sort_order: a.sort_order });
|
||||
// Swap the two items in a copy, then renumber ALL items by their new
|
||||
// index position (index * 10). This guarantees the sort_order values
|
||||
// change even when both items previously shared the same value (e.g. 0).
|
||||
const reordered = [...combinedWidgets];
|
||||
const tmp = reordered[index];
|
||||
reordered[index] = reordered[targetIndex];
|
||||
reordered[targetIndex] = tmp;
|
||||
// Sequential (not Promise.all) to avoid cache-invalidation race.
|
||||
for (let i = 0; i < reordered.length; i++) {
|
||||
const item = reordered[i];
|
||||
const newSortOrder = i * 10;
|
||||
const refId = (item as { _ref_id?: string })._ref_id;
|
||||
if (refId) {
|
||||
await updateRef.mutateAsync({
|
||||
referenceId: refId,
|
||||
sortOrder: newSortOrder,
|
||||
});
|
||||
} else {
|
||||
await saveWidget.mutateAsync({ ...item, sort_order: newSortOrder });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,6 +653,9 @@ export function WidgetConfigDialog({
|
||||
))}
|
||||
{services
|
||||
.filter((s) => s.enabled)
|
||||
// When scoped to a service Overview, only show widgets for THAT
|
||||
// service instance's type (not all services' widgets).
|
||||
.filter((s) => !serviceId || s.id === serviceId)
|
||||
.flatMap((s) =>
|
||||
(SERVICE_REGISTRY[s.service_type]?.widgets ?? []).map((w) => (
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user