fix: reset service editor on switch + add service-page settings shortcut

Settings.tsx: ServiceConfigEditor derived editable state (name, config,
secrets) from the instance prop via useState, but the parent rendered it
without a key. Switching services in the rail reused the same component, so
name/config stayed pinned to the previously selected service while
instance.id/service_type (read live from props) pointed at the new one —
saving then wrote the stale values onto the wrong row (e.g. saving qBittorrent
renamed it "Jellyfin" with Jellyfin's URL). Add key={selectedService.id} so
the editor remounts and resets on switch.

ServicePage: add a Settings shortcut in the header that deep-links to
/settings?tab=services&service=<id>. Settings now reads tab + service query
params (useSearchParams) to open the Services tab with that service
pre-selected, via a new initialServiceId prop on ServicesAdminCard.

Tests: new Settings.services.test.tsx regression test (fails without the key,
passes with it); wrap existing Settings tests in MemoryRouter since Settings
now uses useSearchParams. 166/166 frontend tests pass; typecheck + ESLint clean.
This commit is contained in:
Developer
2026-07-11 11:54:18 +00:00
parent 84dcf9e010
commit dad2202756
8 changed files with 214 additions and 27 deletions
+17 -1
View File
@@ -1,7 +1,9 @@
import { useMemo } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { Settings as SettingsIcon } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useServiceInstances } from "../hooks/useServices";
import type { ServiceInstance } from "../types";
@@ -89,7 +91,21 @@ export function ServicePage() {
<h2 className="text-xl font-semibold">{instance.name}</h2>
<p className="text-sm text-muted-foreground">{binding.description}</p>
</div>
<Badge variant="outline">{binding.name}</Badge>
<div className="flex items-center gap-2">
<Badge variant="outline">{binding.name}</Badge>
<Button
variant="outline"
size="sm"
onClick={() =>
navigate(
`/settings?tab=services&service=${encodeURIComponent(instance.id)}`,
)
}
>
<SettingsIcon className="mr-1 h-3 w-3" />
Settings
</Button>
</div>
</div>
{/* Instance tabs (only when >1 enabled sibling) */}