feat(frontend): add blank Global Settings page (#2218)
* feat(frontend): add blank Global Settings page Add a Global Settings page to the desktop renderer, reachable from the sidebar Settings menu when no project is selected. The project-scoped Settings entry stays when a project is active; otherwise the menu shows Global settings, which opens the new /settings route. The page body is intentionally empty for now and will be filled in incrementally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): regenerate route tree + test Global Settings menu Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bc84b6d2a3
commit
14030772b9
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { DashboardSubhead } from "./DashboardSubhead";
|
||||||
|
|
||||||
|
// App-wide settings, shown from the sidebar when no project is selected. The
|
||||||
|
// body is intentionally empty for now; it will be filled in incrementally.
|
||||||
|
export function GlobalSettingsForm() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
|
||||||
|
<DashboardSubhead title="Global settings" subtitle="Settings that apply across all projects" />
|
||||||
|
<div className="min-h-0 flex-1 overflow-y-auto p-[18px]" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -135,6 +135,16 @@ describe("Sidebar", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("opens global settings from the footer menu when no project is selected", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderSidebar();
|
||||||
|
|
||||||
|
await user.click(screen.getAllByLabelText("Settings")[0]);
|
||||||
|
await user.click(await screen.findByRole("menuitem", { name: "Global settings" }));
|
||||||
|
|
||||||
|
expect(navigateMock).toHaveBeenCalledWith({ to: "/settings" });
|
||||||
|
});
|
||||||
|
|
||||||
it("always shows action icons and reserves padding for them", () => {
|
it("always shows action icons and reserves padding for them", () => {
|
||||||
renderSidebar();
|
renderSidebar();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ function useSelection() {
|
||||||
activeSessionId: params.sessionId,
|
activeSessionId: params.sessionId,
|
||||||
goHome: () => void navigate({ to: "/" }),
|
goHome: () => void navigate({ to: "/" }),
|
||||||
goPrs: () => void navigate({ to: "/prs" }),
|
goPrs: () => void navigate({ to: "/prs" }),
|
||||||
|
goGlobalSettings: () => void navigate({ to: "/settings" }),
|
||||||
goSettings: (projectId: string) => void navigate({ to: "/projects/$projectId/settings", params: { projectId } }),
|
goSettings: (projectId: string) => void navigate({ to: "/projects/$projectId/settings", params: { projectId } }),
|
||||||
goProject: (projectId: string) => void navigate({ to: "/projects/$projectId", params: { projectId } }),
|
goProject: (projectId: string) => void navigate({ to: "/projects/$projectId", params: { projectId } }),
|
||||||
goSession: (projectId: string, sessionId: string) =>
|
goSession: (projectId: string, sessionId: string) =>
|
||||||
|
|
@ -284,14 +285,17 @@ export function Sidebar({
|
||||||
Search
|
Search
|
||||||
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{selection.activeProjectId && (
|
<DropdownMenuSeparator />
|
||||||
<>
|
{selection.activeProjectId ? (
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
|
||||||
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
|
<Settings aria-hidden="true" />
|
||||||
<Settings aria-hidden="true" />
|
Project settings
|
||||||
Project settings
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
) : (
|
||||||
</>
|
<DropdownMenuItem onSelect={selection.goGlobalSettings}>
|
||||||
|
<Settings aria-hidden="true" />
|
||||||
|
Global settings
|
||||||
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
@ -342,14 +346,17 @@ export function Sidebar({
|
||||||
Search
|
Search
|
||||||
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{selection.activeProjectId && (
|
<DropdownMenuSeparator />
|
||||||
<>
|
{selection.activeProjectId ? (
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
|
||||||
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
|
<Settings aria-hidden="true" />
|
||||||
<Settings aria-hidden="true" />
|
Project settings
|
||||||
Project settings
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
) : (
|
||||||
</>
|
<DropdownMenuItem onSelect={selection.goGlobalSettings}>
|
||||||
|
<Settings aria-hidden="true" />
|
||||||
|
Global settings
|
||||||
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
import { Route as rootRouteImport } from "./routes/__root";
|
import { Route as rootRouteImport } from "./routes/__root";
|
||||||
import { Route as ShellRouteImport } from "./routes/_shell";
|
import { Route as ShellRouteImport } from "./routes/_shell";
|
||||||
import { Route as ShellIndexRouteImport } from "./routes/_shell.index";
|
import { Route as ShellIndexRouteImport } from "./routes/_shell.index";
|
||||||
|
import { Route as ShellSettingsRouteImport } from "./routes/_shell.settings";
|
||||||
import { Route as ShellPrsRouteImport } from "./routes/_shell.prs";
|
import { Route as ShellPrsRouteImport } from "./routes/_shell.prs";
|
||||||
import { Route as ShellSessionsSessionIdRouteImport } from "./routes/_shell.sessions.$sessionId";
|
import { Route as ShellSessionsSessionIdRouteImport } from "./routes/_shell.sessions.$sessionId";
|
||||||
import { Route as ShellProjectsProjectIdRouteImport } from "./routes/_shell.projects.$projectId";
|
import { Route as ShellProjectsProjectIdRouteImport } from "./routes/_shell.projects.$projectId";
|
||||||
|
|
@ -26,6 +27,11 @@ const ShellIndexRoute = ShellIndexRouteImport.update({
|
||||||
path: "/",
|
path: "/",
|
||||||
getParentRoute: () => ShellRoute,
|
getParentRoute: () => ShellRoute,
|
||||||
} as any);
|
} as any);
|
||||||
|
const ShellSettingsRoute = ShellSettingsRouteImport.update({
|
||||||
|
id: "/settings",
|
||||||
|
path: "/settings",
|
||||||
|
getParentRoute: () => ShellRoute,
|
||||||
|
} as any);
|
||||||
const ShellPrsRoute = ShellPrsRouteImport.update({
|
const ShellPrsRoute = ShellPrsRouteImport.update({
|
||||||
id: "/prs",
|
id: "/prs",
|
||||||
path: "/prs",
|
path: "/prs",
|
||||||
|
|
@ -55,6 +61,7 @@ const ShellProjectsProjectIdSessionsSessionIdRoute = ShellProjectsProjectIdSessi
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
"/": typeof ShellIndexRoute;
|
"/": typeof ShellIndexRoute;
|
||||||
"/prs": typeof ShellPrsRoute;
|
"/prs": typeof ShellPrsRoute;
|
||||||
|
"/settings": typeof ShellSettingsRoute;
|
||||||
"/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
"/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
||||||
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
||||||
"/projects/$projectId/settings": typeof ShellProjectsProjectIdSettingsRoute;
|
"/projects/$projectId/settings": typeof ShellProjectsProjectIdSettingsRoute;
|
||||||
|
|
@ -62,6 +69,7 @@ export interface FileRoutesByFullPath {
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
"/prs": typeof ShellPrsRoute;
|
"/prs": typeof ShellPrsRoute;
|
||||||
|
"/settings": typeof ShellSettingsRoute;
|
||||||
"/": typeof ShellIndexRoute;
|
"/": typeof ShellIndexRoute;
|
||||||
"/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
"/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
||||||
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
||||||
|
|
@ -72,6 +80,7 @@ export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport;
|
__root__: typeof rootRouteImport;
|
||||||
"/_shell": typeof ShellRouteWithChildren;
|
"/_shell": typeof ShellRouteWithChildren;
|
||||||
"/_shell/prs": typeof ShellPrsRoute;
|
"/_shell/prs": typeof ShellPrsRoute;
|
||||||
|
"/_shell/settings": typeof ShellSettingsRoute;
|
||||||
"/_shell/": typeof ShellIndexRoute;
|
"/_shell/": typeof ShellIndexRoute;
|
||||||
"/_shell/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
"/_shell/projects/$projectId": typeof ShellProjectsProjectIdRoute;
|
||||||
"/_shell/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
"/_shell/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
|
||||||
|
|
@ -83,6 +92,7 @@ export interface FileRouteTypes {
|
||||||
fullPaths:
|
fullPaths:
|
||||||
| "/"
|
| "/"
|
||||||
| "/prs"
|
| "/prs"
|
||||||
|
| "/settings"
|
||||||
| "/projects/$projectId"
|
| "/projects/$projectId"
|
||||||
| "/sessions/$sessionId"
|
| "/sessions/$sessionId"
|
||||||
| "/projects/$projectId/settings"
|
| "/projects/$projectId/settings"
|
||||||
|
|
@ -90,6 +100,7 @@ export interface FileRouteTypes {
|
||||||
fileRoutesByTo: FileRoutesByTo;
|
fileRoutesByTo: FileRoutesByTo;
|
||||||
to:
|
to:
|
||||||
| "/prs"
|
| "/prs"
|
||||||
|
| "/settings"
|
||||||
| "/"
|
| "/"
|
||||||
| "/projects/$projectId"
|
| "/projects/$projectId"
|
||||||
| "/sessions/$sessionId"
|
| "/sessions/$sessionId"
|
||||||
|
|
@ -99,6 +110,7 @@ export interface FileRouteTypes {
|
||||||
| "__root__"
|
| "__root__"
|
||||||
| "/_shell"
|
| "/_shell"
|
||||||
| "/_shell/prs"
|
| "/_shell/prs"
|
||||||
|
| "/_shell/settings"
|
||||||
| "/_shell/"
|
| "/_shell/"
|
||||||
| "/_shell/projects/$projectId"
|
| "/_shell/projects/$projectId"
|
||||||
| "/_shell/sessions/$sessionId"
|
| "/_shell/sessions/$sessionId"
|
||||||
|
|
@ -126,6 +138,13 @@ declare module "@tanstack/react-router" {
|
||||||
preLoaderRoute: typeof ShellIndexRouteImport;
|
preLoaderRoute: typeof ShellIndexRouteImport;
|
||||||
parentRoute: typeof ShellRoute;
|
parentRoute: typeof ShellRoute;
|
||||||
};
|
};
|
||||||
|
"/_shell/settings": {
|
||||||
|
id: "/_shell/settings";
|
||||||
|
path: "/settings";
|
||||||
|
fullPath: "/settings";
|
||||||
|
preLoaderRoute: typeof ShellSettingsRouteImport;
|
||||||
|
parentRoute: typeof ShellRoute;
|
||||||
|
};
|
||||||
"/_shell/prs": {
|
"/_shell/prs": {
|
||||||
id: "/_shell/prs";
|
id: "/_shell/prs";
|
||||||
path: "/prs";
|
path: "/prs";
|
||||||
|
|
@ -166,6 +185,7 @@ declare module "@tanstack/react-router" {
|
||||||
|
|
||||||
interface ShellRouteChildren {
|
interface ShellRouteChildren {
|
||||||
ShellPrsRoute: typeof ShellPrsRoute;
|
ShellPrsRoute: typeof ShellPrsRoute;
|
||||||
|
ShellSettingsRoute: typeof ShellSettingsRoute;
|
||||||
ShellIndexRoute: typeof ShellIndexRoute;
|
ShellIndexRoute: typeof ShellIndexRoute;
|
||||||
ShellProjectsProjectIdRoute: typeof ShellProjectsProjectIdRoute;
|
ShellProjectsProjectIdRoute: typeof ShellProjectsProjectIdRoute;
|
||||||
ShellSessionsSessionIdRoute: typeof ShellSessionsSessionIdRoute;
|
ShellSessionsSessionIdRoute: typeof ShellSessionsSessionIdRoute;
|
||||||
|
|
@ -175,6 +195,7 @@ interface ShellRouteChildren {
|
||||||
|
|
||||||
const ShellRouteChildren: ShellRouteChildren = {
|
const ShellRouteChildren: ShellRouteChildren = {
|
||||||
ShellPrsRoute: ShellPrsRoute,
|
ShellPrsRoute: ShellPrsRoute,
|
||||||
|
ShellSettingsRoute: ShellSettingsRoute,
|
||||||
ShellIndexRoute: ShellIndexRoute,
|
ShellIndexRoute: ShellIndexRoute,
|
||||||
ShellProjectsProjectIdRoute: ShellProjectsProjectIdRoute,
|
ShellProjectsProjectIdRoute: ShellProjectsProjectIdRoute,
|
||||||
ShellSessionsSessionIdRoute: ShellSessionsSessionIdRoute,
|
ShellSessionsSessionIdRoute: ShellSessionsSessionIdRoute,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { GlobalSettingsForm } from "../components/GlobalSettingsForm";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_shell/settings")({
|
||||||
|
component: GlobalSettingsForm,
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue