refactor(views): extract shared NewWorkspacePage shell

The web (/new-workspace) and desktop (NewWorkspaceRoute) pages had
identical outer layout — same container, heading, and copy — with only
the onSuccess navigation primitive differing. That's exactly the
No-Duplication Rule pattern: extract the shared UI, inject the
platform-specific behavior.

The apps now only own the thin auth guard (web needs it, desktop
routes below WorkspaceRouteLayout already handle it) and the
onSuccess → navigate call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing
2026-04-16 18:58:17 +08:00
parent 230f20df0b
commit ca8ef0e1e7
4 changed files with 42 additions and 33 deletions

View File

@@ -20,7 +20,7 @@ import { DaemonRuntimeCard } from "./components/daemon-runtime-card";
import { AgentsPage } from "@multica/views/agents";
import { InboxPage } from "@multica/views/inbox";
import { SettingsPage } from "@multica/views/settings";
import { CreateWorkspaceForm } from "@multica/views/workspace/create-workspace-form";
import { NewWorkspacePage } from "@multica/views/workspace/new-workspace-page";
import { InvitePage } from "@multica/views/invite";
import { useNavigation } from "@multica/views/navigation";
import { paths } from "@multica/core/paths";
@@ -62,21 +62,9 @@ function PageShell() {
function NewWorkspaceRoute() {
const nav = useNavigation();
return (
<div className="flex min-h-svh flex-col items-center justify-center bg-background px-6 py-12">
<div className="flex w-full max-w-md flex-col items-center gap-6">
<div className="text-center">
<h1 className="text-3xl font-semibold tracking-tight">
Welcome to Multica
</h1>
<p className="mt-2 text-muted-foreground">
Create your workspace to get started.
</p>
</div>
<CreateWorkspaceForm
onSuccess={(ws) => nav.push(paths.workspace(ws.slug).issues())}
/>
</div>
</div>
<NewWorkspacePage
onSuccess={(ws) => nav.push(paths.workspace(ws.slug).issues())}
/>
);
}

View File

@@ -4,9 +4,9 @@ import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useAuthStore } from "@multica/core/auth";
import { paths } from "@multica/core/paths";
import { CreateWorkspaceForm } from "@multica/views/workspace/create-workspace-form";
import { NewWorkspacePage } from "@multica/views/workspace/new-workspace-page";
export default function NewWorkspacePage() {
export default function Page() {
const router = useRouter();
const user = useAuthStore((s) => s.user);
const isLoading = useAuthStore((s) => s.isLoading);
@@ -18,20 +18,8 @@ export default function NewWorkspacePage() {
if (isLoading || !user) return null;
return (
<div className="flex min-h-svh flex-col items-center justify-center bg-background px-6 py-12">
<div className="flex w-full max-w-md flex-col items-center gap-6">
<div className="text-center">
<h1 className="text-3xl font-semibold tracking-tight">
Welcome to Multica
</h1>
<p className="mt-2 text-muted-foreground">
Create your workspace to get started.
</p>
</div>
<CreateWorkspaceForm
onSuccess={(ws) => router.push(paths.workspace(ws.slug).issues())}
/>
</div>
</div>
<NewWorkspacePage
onSuccess={(ws) => router.push(paths.workspace(ws.slug).issues())}
/>
);
}

View File

@@ -30,6 +30,7 @@
"./workspace/workspace-avatar": "./workspace/workspace-avatar.tsx",
"./workspace/create-workspace-form": "./workspace/create-workspace-form.tsx",
"./workspace/no-access-page": "./workspace/no-access-page.tsx",
"./workspace/new-workspace-page": "./workspace/new-workspace-page.tsx",
"./workspace/use-workspace-seen": "./workspace/use-workspace-seen.ts",
"./layout": "./layout/index.ts",
"./auth": "./auth/index.ts",

View File

@@ -0,0 +1,32 @@
"use client";
import type { Workspace } from "@multica/core/types";
import { CreateWorkspaceForm } from "./create-workspace-form";
/**
* Full-page shell for the /new-workspace route. Shared between web
* (Next.js) and desktop (react-router) so the two apps can't drift.
* Callers provide the onSuccess handler — that's the only app-specific
* piece, because each app uses its own navigation primitive.
*/
export function NewWorkspacePage({
onSuccess,
}: {
onSuccess: (workspace: Workspace) => void;
}) {
return (
<div className="flex min-h-svh flex-col items-center justify-center bg-background px-6 py-12">
<div className="flex w-full max-w-md flex-col items-center gap-6">
<div className="text-center">
<h1 className="text-3xl font-semibold tracking-tight">
Welcome to Multica
</h1>
<p className="mt-2 text-muted-foreground">
Create your workspace to get started.
</p>
</div>
<CreateWorkspaceForm onSuccess={onSuccess} />
</div>
</div>
);
}