From ca8ef0e1e7f2cd134ebd114e1c852f152fdc1108 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:58:17 +0800 Subject: [PATCH] refactor(views): extract shared NewWorkspacePage shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- apps/desktop/src/renderer/src/routes.tsx | 20 +++--------- apps/web/app/(auth)/new-workspace/page.tsx | 22 +++---------- packages/views/package.json | 1 + .../views/workspace/new-workspace-page.tsx | 32 +++++++++++++++++++ 4 files changed, 42 insertions(+), 33 deletions(-) create mode 100644 packages/views/workspace/new-workspace-page.tsx diff --git a/apps/desktop/src/renderer/src/routes.tsx b/apps/desktop/src/renderer/src/routes.tsx index 8fa6b5385..61ae74f5c 100644 --- a/apps/desktop/src/renderer/src/routes.tsx +++ b/apps/desktop/src/renderer/src/routes.tsx @@ -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 ( -
-
-
-

- Welcome to Multica -

-

- Create your workspace to get started. -

-
- nav.push(paths.workspace(ws.slug).issues())} - /> -
-
+ nav.push(paths.workspace(ws.slug).issues())} + /> ); } diff --git a/apps/web/app/(auth)/new-workspace/page.tsx b/apps/web/app/(auth)/new-workspace/page.tsx index f39039c62..9107512b3 100644 --- a/apps/web/app/(auth)/new-workspace/page.tsx +++ b/apps/web/app/(auth)/new-workspace/page.tsx @@ -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 ( -
-
-
-

- Welcome to Multica -

-

- Create your workspace to get started. -

-
- router.push(paths.workspace(ws.slug).issues())} - /> -
-
+ router.push(paths.workspace(ws.slug).issues())} + /> ); } diff --git a/packages/views/package.json b/packages/views/package.json index 0fffc1ac0..71ee90261 100644 --- a/packages/views/package.json +++ b/packages/views/package.json @@ -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", diff --git a/packages/views/workspace/new-workspace-page.tsx b/packages/views/workspace/new-workspace-page.tsx new file mode 100644 index 000000000..a5ccca43c --- /dev/null +++ b/packages/views/workspace/new-workspace-page.tsx @@ -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 ( +
+
+
+

+ Welcome to Multica +

+

+ Create your workspace to get started. +

+
+ +
+
+ ); +}