refactor: enhance onboarding logic and update default redirect path

This commit is contained in:
Abhimanyu Saharan
2026-02-09 22:20:19 +05:30
parent f8860bbc71
commit 6f76e430f4
6 changed files with 97 additions and 12 deletions

View File

@@ -20,17 +20,11 @@ import {
useGetMeApiV1UsersMeGet,
useUpdateMeApiV1UsersMePatch,
} from "@/api/generated/users/users";
import type { UserRead } from "@/api/generated/model";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import SearchableSelect from "@/components/ui/searchable-select";
const isCompleteProfile = (profile: UserRead | null | undefined) => {
if (!profile) return false;
const resolvedName = profile.preferred_name?.trim() || profile.name?.trim();
return Boolean(resolvedName) && Boolean(profile.timezone?.trim());
};
import { isOnboardingComplete } from "@/lib/onboarding";
export default function OnboardingPage() {
const router = useRouter();
@@ -113,7 +107,7 @@ export default function OnboardingPage() {
);
useEffect(() => {
if (profile && isCompleteProfile(profile)) {
if (profile && isOnboardingComplete(profile)) {
router.replace("/dashboard");
}
}, [profile, router]);

View File

@@ -13,8 +13,8 @@ describe("resolveSignInRedirectUrl", () => {
expect(resolveSignInRedirectUrl(null)).toBe("/boards");
});
it("defaults to /dashboard when no env fallback is set", () => {
expect(resolveSignInRedirectUrl(null)).toBe("/dashboard");
it("defaults to /onboarding when no env fallback is set", () => {
expect(resolveSignInRedirectUrl(null)).toBe("/onboarding");
});
it("allows safe relative paths", () => {

View File

@@ -1,4 +1,4 @@
const DEFAULT_SIGN_IN_REDIRECT = "/dashboard";
const DEFAULT_SIGN_IN_REDIRECT = "/onboarding";
function isSafeRelativePath(value: string): boolean {
return value.startsWith("/") && !value.startsWith("//");

View File

@@ -2,17 +2,48 @@
import { useEffect } from "react";
import type { ReactNode } from "react";
import { usePathname, useRouter } from "next/navigation";
import { SignedIn, useUser } from "@/auth/clerk";
import { SignedIn, useAuth, useUser } from "@/auth/clerk";
import { ApiError } from "@/api/mutator";
import {
type getMeApiV1UsersMeGetResponse,
useGetMeApiV1UsersMeGet,
} from "@/api/generated/users/users";
import { BrandMark } from "@/components/atoms/BrandMark";
import { OrgSwitcher } from "@/components/organisms/OrgSwitcher";
import { UserMenu } from "@/components/organisms/UserMenu";
import { isOnboardingComplete } from "@/lib/onboarding";
export function DashboardShell({ children }: { children: ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const { isSignedIn } = useAuth();
const { user } = useUser();
const displayName =
user?.fullName ?? user?.firstName ?? user?.username ?? "Operator";
const isOnboardingPath = pathname === "/onboarding";
const meQuery = useGetMeApiV1UsersMeGet<
getMeApiV1UsersMeGetResponse,
ApiError
>({
query: {
enabled: Boolean(isSignedIn) && !isOnboardingPath,
retry: false,
refetchOnMount: "always",
},
});
const profile = meQuery.data?.status === 200 ? meQuery.data.data : null;
useEffect(() => {
if (!isSignedIn || isOnboardingPath) return;
if (!profile) return;
if (!isOnboardingComplete(profile)) {
router.replace("/onboarding");
}
}, [isOnboardingPath, isSignedIn, profile, router]);
useEffect(() => {
if (typeof window === "undefined") return;

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { isOnboardingComplete } from "@/lib/onboarding";
describe("isOnboardingComplete", () => {
it("returns false when profile is missing", () => {
expect(isOnboardingComplete(null)).toBe(false);
expect(isOnboardingComplete(undefined)).toBe(false);
});
it("returns false when timezone is missing", () => {
expect(
isOnboardingComplete({
preferred_name: "Asha",
timezone: "",
}),
).toBe(false);
});
it("returns false when both name fields are missing", () => {
expect(
isOnboardingComplete({
name: " ",
preferred_name: " ",
timezone: "America/New_York",
}),
).toBe(false);
});
it("accepts preferred_name + timezone", () => {
expect(
isOnboardingComplete({
preferred_name: "Asha",
timezone: "America/New_York",
}),
).toBe(true);
});
it("accepts fallback name + timezone", () => {
expect(
isOnboardingComplete({
name: "Asha",
timezone: "America/New_York",
}),
).toBe(true);
});
});

View File

@@ -0,0 +1,13 @@
type OnboardingProfileLike = {
name?: string | null;
preferred_name?: string | null;
timezone?: string | null;
};
export function isOnboardingComplete(
profile: OnboardingProfileLike | null | undefined,
): boolean {
if (!profile) return false;
const resolvedName = profile.preferred_name?.trim() || profile.name?.trim();
return Boolean(resolvedName) && Boolean(profile.timezone?.trim());
}