-
Notifications
You must be signed in to change notification settings - Fork 1.4k
web: new onboarding #1188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
web: new onboarding #1188
Changes from 46 commits
3f5c7bc
2c4c410
e68957c
e3b25de
ae3ef1d
9b26d12
8d4f42d
08ff461
6f59fed
f5e1c65
609278a
0f3dda7
d4c17c4
952f019
badaa37
7da87fe
73bc732
a42f264
cdbeced
c52cf61
b81659a
906d278
c70d720
2a1b233
6594be4
4cfeedd
5ee586f
9fb84d0
44aff44
724a038
b54a556
9088554
dc24b11
8a644fe
0d602f7
47c0b9f
7d3645a
f9e1710
c56f999
a8ab75b
63d06c2
6c56457
3004f01
60ec6de
6facdf6
a6f3843
8e7372a
16c5b6b
15a0935
9858c08
a2a136c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { getCurrentUser } from "@cap/database/auth/session"; | ||
| import { redirect } from "next/navigation"; | ||
|
|
||
| export default async function OnboardingStepLayout({ | ||
| children, | ||
| params, | ||
| }: { | ||
| children: React.ReactNode; | ||
| params: Promise<{ steps: string[] }>; | ||
| }) { | ||
| const user = await getCurrentUser(); | ||
|
|
||
| if (!user) { | ||
| redirect("/login"); | ||
| } | ||
|
|
||
| const steps = user.onboardingSteps || {}; | ||
| const currentStep = (await params).steps?.[0] ?? "welcome"; | ||
|
|
||
| const ordered = [ | ||
| "welcome", | ||
| "organization-setup", | ||
| "custom-domain", | ||
| "invite-team", | ||
| "download", | ||
| ] as const; | ||
| const isComplete = (s: (typeof ordered)[number]) => | ||
| s === "welcome" | ||
| ? Boolean(steps.welcome && user.name) | ||
| : s === "organization-setup" | ||
| ? Boolean(steps.organizationSetup) | ||
| : s === "custom-domain" | ||
| ? Boolean(steps.customDomain) | ||
| : s === "invite-team" | ||
| ? Boolean(steps.inviteTeam) | ||
| : Boolean(steps.download); | ||
|
|
||
| if (isComplete("download")) redirect("/dashboard/caps"); | ||
|
|
||
| const firstIncomplete = ordered.find((s) => !isComplete(s)) ?? "download"; | ||
|
|
||
| if (currentStep !== firstIncomplete) { | ||
| redirect(`/onboarding/${firstIncomplete}`); | ||
| } | ||
|
|
||
| return children; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { getCurrentUser } from "@cap/database/auth/session"; | ||
| import { CustomDomainPage } from "../components/CustomDomainPage"; | ||
| import { DownloadPage } from "../components/Download"; | ||
| import { InviteTeamPage } from "../components/InviteTeamPage"; | ||
| import { OrganizationSetupPage } from "../components/OrganizationSetupPage"; | ||
| import { WelcomePage } from "../components/WelcomePage"; | ||
|
|
||
| export default async function OnboardingStepPage({ | ||
| params, | ||
| }: { | ||
| params: Promise<{ | ||
| steps: "welcome" | "organization-setup" | "custom-domain" | "invite-team"; | ||
| }>; | ||
| }) { | ||
| const user = await getCurrentUser(); | ||
| const step = (await params).steps[0]; | ||
|
Comment on lines
+8
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chainFix Next.js params typing (not a Promise) and steps type (string[]) Next App Router pages receive a plain object, not a Promise. Also, steps is an array for a catch‑all segment. Accessing [0] on a string union is incorrect. Apply: -export default async function OnboardingStepPage({
- params,
-}: {
- params: Promise<{
- steps: "welcome" | "organization-setup" | "custom-domain" | "invite-team";
- }>;
-}) {
- const step = (await params).steps[0];
+export default async function OnboardingStepPage({
+ params,
+}: {
+ params: { steps: string[] };
+}) {
+ const step = params.steps?.[0];Run to find other mis-typed route params: 🏁 Script executed: #!/bin/bash
rg -n --type=ts --type=tsx -C2 'params:\s*Promise<'Length of output: 27 🏁 Script executed: #!/bin/bash
rg -nP 'params:\s*Promise<' -g '*.ts' -g '*.tsx' -C2Length of output: 3968 Fix params typing in App Router pages and route handlers
export default async function Page({ params }: { params: { slug: string[] } }) {
const slug = params.slug[0];
…
}and for handlers: export async function GET(request: NextRequest, { params }: { params: { platform: string } }) { … }🤖 Prompt for AI Agents |
||
|
|
||
| switch (step) { | ||
| case "welcome": | ||
| return <WelcomePage />; | ||
| case "organization-setup": | ||
| return <OrganizationSetupPage user={user} />; | ||
|
Check failure on line 22 in apps/web/app/(org)/onboarding/[...steps]/page.tsx
|
||
| case "custom-domain": | ||
| return <CustomDomainPage />; | ||
| case "invite-team": | ||
| return <InviteTeamPage />; | ||
| case "download": | ||
| return <DownloadPage />; | ||
| default: | ||
| return null; | ||
| } | ||
| } | ||
|
ameer2468 marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| "use client"; | ||
|
|
||
| import { LogoBadge } from "@cap/ui"; | ||
| import { faArrowLeft } from "@fortawesome/free-solid-svg-icons"; | ||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||
| import clsx from "clsx"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| export const Base = ({ | ||
| children, | ||
| title, | ||
| description, | ||
| descriptionClassName, | ||
| hideBackButton = true, | ||
| }: { | ||
| children: React.ReactNode; | ||
| title: string; | ||
| description: string | React.ReactNode; | ||
| descriptionClassName?: string; | ||
| hideBackButton?: boolean; | ||
| }) => { | ||
| const router = useRouter(); | ||
| return ( | ||
| <div className="relative w-[calc(100%-2%)] space-y-7 p-7 max-w-[472px] bg-gray-2 border border-gray-4 rounded-2xl"> | ||
| {!hideBackButton && ( | ||
| <div | ||
| onClick={() => router.back()} | ||
| className="absolute overflow-hidden flex top-5 rounded-full left-5 z-20 hover:bg-gray-1 gap-2 items-center py-1.5 px-3 text-gray-12 bg-transparent border border-gray-4 transition-colors duration-300 cursor-pointer" | ||
| > | ||
| <FontAwesomeIcon className="w-2" icon={faArrowLeft} /> | ||
| <p className="text-xs text-inherit">Back</p> | ||
| </div> | ||
| )} | ||
| <a href="/"> | ||
| <LogoBadge className="mx-auto w-auto h-12" /> | ||
| </a> | ||
| <div className="flex flex-col justify-center items-center space-y-1 text-center"> | ||
| <h2 className="text-2xl font-semibold text-gray-12">{title}</h2> | ||
| {typeof description === "string" ? ( | ||
| <p | ||
| className={clsx( | ||
| "w-full text-base max-w-[260px] text-gray-10", | ||
| descriptionClassName, | ||
| )} | ||
| > | ||
| {description} | ||
| </p> | ||
| ) : ( | ||
| description | ||
| )} | ||
| </div> | ||
| {children} | ||
| </div> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify onboarding step enforcement is handled elsewhere.
The current code only checks
user.namebefore allowing dashboard access. Given the PR adds multi-step onboarding with tracked steps (welcome, organizationSetup, customDomain, inviteTeam, download as mentioned in the AI summary), verify that:/dashboardafter only completing the welcome stepThe previous version (per past review comments) had checks for individual
onboardingStepsproperties here that were problematic for existing users. If those checks were intentionally moved to the onboarding layout, that's correct. If they were removed without being relocated, incomplete onboarding users could access the dashboard.Run the following script to verify where onboarding step checks are enforced:
🏁 Script executed:
Length of output: 2510
🏁 Script executed:
Length of output: 27
🏁 Script executed:
Length of output: 4530
Enforce full onboarding in dashboard layout.
In
apps/web/app/(org)/dashboard/layout.tsx, add checks foruser.onboardingSteps.organizationSetup,.customDomain, and.inviteTeam(beyond justuser.name) and redirect to the respective onboarding step if any are incomplete; currently onlyuser.nameis gated, allowing dashboard access after only the welcome step.🤖 Prompt for AI Agents