Files
multica/apps/web/features/landing/utils/use-dashboard-cta.ts
Naiyuan Qing dddcff8744 fix(landing): point dashboard CTAs at the real workspace route (#5442)
The four landing "Dashboard" CTAs used href="/" and relied on the proxy
bouncing logged-in visitors from the root path to their workspace. Once #5363
kept "/" public on the official marketing host so logged-in users could browse
the site (#5326), that bounce stopped — and the CTAs began resolving to the
page the visitor was already on, so clicking them did nothing at all.

Resolve the destination in the CTA instead of leaning on a redirect side
effect elsewhere: useDashboardCtaHref() runs the same
resolvePostAuthDestination() that RedirectIfAuthenticated already uses, so
"where the dashboard lives" has one source of truth. This also collapses the
same `user ? "/" : "/login"` line that had been copy-pasted across four files.

proxy.ts and redirect-if-authenticated.tsx are untouched — #5363's behavior is
correct and proxy.test.ts still passes unchanged.

Closes MUL-4794

Co-authored-by: Walt <walt@multica.ai>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-15 14:44:49 +08:00

69 lines
2.2 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { useAuthStore } from "@multica/core/auth";
import { workspaceListOptions } from "@multica/core/workspace";
import {
paths,
resolvePostAuthDestination,
useHasOnboarded,
} from "@multica/core/paths";
import type { Workspace } from "@multica/core/types";
/**
* While the workspace list is in flight the CTA points at `/issues`, which the
* proxy rewrites to the last workspace. That keeps the button working during
* hydration without hiding or disabling it (which would flicker). A visitor
* with no workspace history lands back on the landing page in that window and
* can click again once the list resolves.
*/
const LOADING_FALLBACK_HREF = "/issues";
export function resolveDashboardCtaHref({
isAuthenticated,
isWorkspaceListFetched,
workspaces,
hasOnboarded,
}: {
isAuthenticated: boolean;
isWorkspaceListFetched: boolean;
workspaces: Workspace[] | undefined;
hasOnboarded: boolean;
}): string {
if (!isAuthenticated) return paths.login();
if (!isWorkspaceListFetched || !workspaces) return LOADING_FALLBACK_HREF;
return resolvePostAuthDestination(workspaces, hasOnboarded);
}
/**
* Destination for the landing "Dashboard" CTA.
*
* These CTAs used to point at `/` and lean on the proxy bouncing logged-in
* visitors from the root path to their workspace. Once `/` stayed public on the
* official marketing host, that bounce stopped and the CTA resolved to the page
* the visitor was already on — a click with no visible effect. Resolve the real
* destination here instead, through the same resolver
* `RedirectIfAuthenticated` uses, so "where the dashboard lives" has one source
* of truth.
*
* Shares `workspaceListOptions()`' query key with `RedirectIfAuthenticated`, so
* on the landing page the list is typically already in flight or cached and this
* adds no request.
*/
export function useDashboardCtaHref(): string {
const user = useAuthStore((s) => s.user);
const hasOnboarded = useHasOnboarded();
const { data, isFetched } = useQuery({
...workspaceListOptions(),
enabled: !!user,
});
return resolveDashboardCtaHref({
isAuthenticated: !!user,
isWorkspaceListFetched: isFetched,
workspaces: data,
hasOnboarded,
});
}