mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-28 05:46:58 +02:00
* fix(auth): route invitees to their workspace instead of forcing /onboarding Workspace presence now wins over `onboarded_at` across every post-auth entry point, so a user invited into an existing workspace lands inside that workspace instead of being trapped in the new-workspace wizard. The redesigned onboarding flow (#1411) intentionally flipped the priority during frontend development so every login re-entered /onboarding; the backend `onboarded_at` field shipped but the flipped priority was never restored. Closes #1837. - packages/core/paths/resolve.ts: has-workspace beats !hasOnboarded. Onboarding is reachable only when the user has zero workspaces. - apps/web/app/auth/callback/page.tsx: drop the early-return on !onboarded so a `next=/invite/<id>` survives Google OAuth round-trips. - apps/web/app/(auth)/login/page.tsx: same removal in both the already-authenticated effect and the post-login handler. - packages/views/layout/use-dashboard-guard.ts: stop bouncing in-workspace users to /onboarding; rely on the resolver for zero-workspace cases. - apps/desktop/src/renderer/src/App.tsx: window-overlay now opens onboarding only when wsCount === 0 AND !hasOnboarded. - apps/web/app/(auth)/onboarding/page.tsx: defense-in-depth — bounce away if the visitor already has a workspace, even on direct URL access. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(auth): fix URLSearchParams leaking state across callback tests The previous cleanup `mockSearchParams.forEach((_v, k) => mockSearchParams.delete(k))` silently skipped entries because forEach advances its index while the underlying URLSearchParams shrinks, so a `state=next:/invite/...` set in one test bled into the next. Snapshot keys via Array.from before deleting. Also rewrites the assertions to match the new policy: an unonboarded user with a safe `next=` honors it, with a workspace lands in that workspace, and only with zero workspaces falls back to /onboarding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
33 lines
1.0 KiB
TypeScript
33 lines
1.0 KiB
TypeScript
import type { Workspace } from "../types";
|
|
import { useAuthStore } from "../auth";
|
|
import { paths } from "./paths";
|
|
|
|
/**
|
|
* Priority:
|
|
* has workspace → /<first.slug>/issues
|
|
* zero workspaces && !hasOnboarded → /onboarding
|
|
* zero workspaces && hasOnboarded → /workspaces/new
|
|
*
|
|
* Workspace presence wins over onboarding state: a user invited into an
|
|
* existing workspace must NOT be bounced into the new-workspace wizard
|
|
* just because their personal `onboarded_at` is still null.
|
|
*/
|
|
export function resolvePostAuthDestination(
|
|
workspaces: Workspace[],
|
|
hasOnboarded: boolean,
|
|
): string {
|
|
const first = workspaces[0];
|
|
if (first) {
|
|
return paths.workspace(first.slug).issues();
|
|
}
|
|
return hasOnboarded ? paths.newWorkspace() : paths.onboarding();
|
|
}
|
|
|
|
/**
|
|
* Single source of truth: backed by `users.onboarded_at`, which
|
|
* arrives with the user object on every auth response.
|
|
*/
|
|
export function useHasOnboarded(): boolean {
|
|
return useAuthStore((s) => s.user?.onboarded_at != null);
|
|
}
|