Web console - Pairing/Library/Stats refactored into self-contained subsections that each own their own queries + mutations; a shared slot-based layout (view.tsx) is filled by the live page (containers) and Storybook (pure cards + fixtures) so the layout can't drift. - All paired devices in one list on Pairing with a protocol column (punktfunk/1 + Moonlight), routing each unpair to the right endpoint; the redundant Clients page is removed. - Library: overview grid split from the add/edit form into separate files. - Login screen links out to the docs. Docs - "Console login password" section on every host page (apt/RPM/Bazzite/SteamOS/Windows) plus a new "Forgot your Password?" troubleshooting page, linked from the login screen. - Console served as HTTP/1.1 over TLS (drop the unusable HTTP/3 advertising) across the Bun entry, launchers, systemd units, and packaging. Tooling - Biome now respects .gitignore (stops linting generated code), config migrated to 2.5.1; all lint issues fixed cleanly. Also includes this branch's in-progress host, Apple client, packaging, and CI changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
import Section from "@unom/ui/section";
|
|
import { Plus } from "lucide-react";
|
|
import { type FC, useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useLocale } from "@/lib/i18n";
|
|
import { m } from "@/paraglide/messages";
|
|
import { type FormTarget, GameFormSection } from "./GameForm";
|
|
import { LibraryGridSection } from "./LibraryGrid";
|
|
|
|
// Library = an OVERVIEW grid + a SEPARATE add/edit form, deliberately split into their own files
|
|
// (LibraryGrid / GameForm) so the two concerns never share a component. This container owns only the
|
|
// shared "is the form open, and for what" UI state; the grid and form each own their own data.
|
|
export const SectionLibrary: FC = () => {
|
|
useLocale();
|
|
// null = form hidden; "new" = adding; a GameEntry = editing that custom entry. Keying the form
|
|
// by the target re-seeds its fields when switching add → edit (or between entries).
|
|
const [target, setTarget] = useState<FormTarget | null>(null);
|
|
|
|
return (
|
|
<Section maxWidth={false}>
|
|
<div className="flex flex-col gap-card">
|
|
<div className="flex items-center justify-between gap-4">
|
|
<h1 className="text-2xl font-semibold">{m.library_title()}</h1>
|
|
{target === null && (
|
|
<Button onClick={() => setTarget("new")}>
|
|
<Plus className="size-4" />
|
|
{m.library_add_button()}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{target !== null && (
|
|
<GameFormSection
|
|
key={target === "new" ? "new" : target.id}
|
|
target={target}
|
|
onClose={() => setTarget(null)}
|
|
/>
|
|
)}
|
|
|
|
<LibraryGridSection onEdit={(entry) => setTarget(entry)} />
|
|
</div>
|
|
</Section>
|
|
);
|
|
};
|