Skip to content

refactor: allow projectId prop for ProjectDataProvider#5164

Merged
chronark merged 3 commits intomainfrom
feat/next-onboarding-step
Mar 3, 2026
Merged

refactor: allow projectId prop for ProjectDataProvider#5164
chronark merged 3 commits intomainfrom
feat/next-onboarding-step

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Mar 2, 2026

What does this PR do?

Refactors the project onboarding flow by restructuring the wizard steps and improving the user experience. The onboarding process now includes a new "configure deployment" step and moves from a route group structure to a dedicated onboarding page.

Key changes include:

  • Moves onboarding from (onboarding) route group to dedicated /onboarding page
  • Adds new "configure deployment" step that reuses existing deployment settings components
  • Improves step navigation with back button support and URL-based step initialization
  • Enhances GitHub callback flow to redirect directly to repository selection
  • Makes ProjectDataProvider more flexible by accepting projectId as prop
  • Adds read-only mode to GitHub settings component
  • Improves repository selection with loading states and success notifications

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Navigate to the projects onboarding page and verify the step wizard flows correctly
  • Test GitHub integration by connecting a repository and ensuring proper redirection
  • Verify the configure deployment step displays settings correctly in read-only mode
  • Test back navigation between steps
  • Confirm repository selection shows loading states and success messages

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@ogzhanolguncu ogzhanolguncu mentioned this pull request Mar 2, 2026
19 tasks
@vercel
Copy link

vercel bot commented Mar 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Mar 3, 2026 11:27am

Request Review

@ogzhanolguncu ogzhanolguncu mentioned this pull request Mar 2, 2026
19 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Replaces the legacy onboarding UI with a new client-side multi-step onboarding wizard (create project → connect GitHub → select repo → configure deployment), removes the old OnboardingHeader, adds OnboardingStepHeader and ConfigureDeployment step, introduces DeploymentSettings, updates ProjectDataProvider to accept projectId prop, and adjusts GitHub callback/navigation and related UI behaviors.

Changes

Cohort / File(s) Summary
Onboarding — removed legacy
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx, web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
Deleted the previous Onboarding component and its OnboardingHeader UI and types.
Onboarding — new wizard & page
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx, web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/page.tsx, web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-step-header.tsx
Added a client Onboarding wizard with step gating by projectId, a server page that gates access on workspace.betaFeatures.deployments, and a new OnboardingStepHeader (icon row, optional back).
Onboarding — steps & helpers
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/create-project.tsx, .../connect-github.tsx, .../select-repo/index.tsx, .../select-repo/repo-list-item.tsx, .../configure-deployment.tsx, .../onboarding-links.tsx
Updated step imports and props (projectId now required in several steps), added ConfigureDeployment step, added dismissible repo-linked banner, per-repo mutation/loading state, trpc invalidation, flow navigation via step wizard, and minor layout/accessibility adjustments.
Settings — deployment & GitHub read-only
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx, .../settings/page.tsx, .../build-settings/github-settings/index.tsx
Added DeploymentSettings component and page composition; settings page now uses DeploymentSettings; GitHub settings gain a readOnly branch that renders a disabled selected-repo card.
Project context provider
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/data-provider.tsx
ProjectDataProvider now accepts an optional projectId prop and falls back to the [projectId] route param; error message updated accordingly.
Projects client & GitHub callback
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/projects-client.tsx, web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx
Removed onboarding query gating from ProjectsClient; GitHub callback mutation now navigates to onboarding select-repo with projectId on success via router.
UI/infra small changes
web/internal/ui/src/components/step-wizard/step-wizard.tsx, web/apps/dashboard/lib/trpc/routers/deploy/environment-settings/sentinel/update-middleware.ts
StepWizard now collapses width (w-0) for inactive steps; minor formatting refactor in middleware.
Styling tweaks
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-links.tsx
Added shadow utility classes to button styles.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser (User)
  participant Page as Server (OnboardingPage)
  participant Client as Onboarding (Client)
  participant API as trpc/API
  participant GH as GitHub

  Note over Page: Server checks workspace.betaFeatures.deployments
  Browser->>Page: GET /{workspace}/projects/onboarding
  Page-->>Browser: HTML (mounts Onboarding client via Suspense)
  Browser->>Client: Create project action
  Client->>API: createProject mutation
  API-->>Client: returns projectId
  Client->>Client: store projectId, advance step
  Browser->>Client: Connect GitHub action
  Client->>API: registerInstallation (initiates OAuth)
  API->>GH: redirect for GitHub OAuth
  GH-->>API: OAuth callback
  API->>Browser: onSuccess triggers client navigation to ?step=select-repo&projectId=...
  Client->>API: list repos / getInstallations
  Browser->>Client: Select repo
  Client->>API: link repo (mutateAsync)
  API-->>Client: success -> invalidation (trpcUtils)
  Client->>Client: advance to configure-deployment
  Browser->>Client: Configure & Deploy
  Client->>API: deploy mutation
  API-->>Client: deploy result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor: allow projectId prop for ProjectDataProvider' is specific and directly related to a key change in the PR, but it covers only one aspect of a much larger refactor that includes restructuring the entire onboarding flow, adding new steps, and moving components.
Description check ✅ Passed The PR description covers all major changes with clear explanation of objectives, includes proper type selection, provides testing instructions, but the required checklist items are largely unchecked, suggesting incomplete compliance with repository requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/next-onboarding-step

Comment @coderabbitai help to get the list of available commands and usage tips.

@ogzhanolguncu ogzhanolguncu mentioned this pull request Mar 2, 2026
19 tasks
@ogzhanolguncu ogzhanolguncu marked this pull request as ready for review March 2, 2026 17:18
@ogzhanolguncu ogzhanolguncu force-pushed the feat/next-onboarding-step branch from 8b8a6bb to bad2566 Compare March 2, 2026 17:49
@ogzhanolguncu ogzhanolguncu force-pushed the feat/next-onboarding-step branch from bad2566 to aec8a3c Compare March 2, 2026 17:55
@ogzhanolguncu ogzhanolguncu force-pushed the feat/next-onboarding-step branch from aec8a3c to 6ba9cf1 Compare March 2, 2026 18:03
@graphite-app graphite-app bot changed the base branch from feat/onboarding-page to graphite-base/5164 March 3, 2026 07:40
@ogzhanolguncu ogzhanolguncu force-pushed the feat/next-onboarding-step branch from 6ba9cf1 to 65370cb Compare March 3, 2026 07:44
@graphite-app graphite-app bot changed the base branch from graphite-base/5164 to main March 3, 2026 07:44
@graphite-app
Copy link

graphite-app bot commented Mar 3, 2026

Merge activity

  • Mar 3, 7:44 AM UTC: Graphite rebased this pull request, because this pull request is set to merge when ready.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx (1)

102-107: ⚠️ Potential issue | 🟡 Minor

Guard clicks while selecting to avoid duplicate submissions.

At Line 105 and Line 107, consider treating loading as a hard interaction lock in this component as well, so repeated clicks can’t re-fire onSelect during in-flight selection.

Suggested fix
         <Button
           variant="outline"
           className="rounded-lg border-grayA-4 shadow-md transition-all h-7"
-          disabled={disabled || isLoading}
+          disabled={disabled || isLoading || loading}
           loading={loading}
-          onClick={() => onSelect(repo)}
+          onClick={() => {
+            if (loading) return;
+            onSelect(repo);
+          }}
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx
around lines 102 - 107, The Button currently uses disabled={disabled ||
isLoading} but still passes onClick={() => onSelect(repo)} which allows repeated
clicks during an in-flight selection; update the component to treat loading as a
hard interaction lock by including the component's loading prop in the disabled
condition (e.g., disabled when loading || isLoading || disabled) and add a
runtime guard at the start of the onClick handler (check loading || isLoading ||
disabled and return early) before calling onSelect(repo) so duplicate
submissions cannot be fired; locate the Button element and its onClick prop in
repo-list-item.tsx to apply these changes.
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx (1)

55-75: ⚠️ Potential issue | 🟠 Major

Apply readOnly behavior consistently across all connection states.

readOnly is only enforced in the "connected" branch. In "no-app" and "no-repo", users still get interactive actions, which breaks the read-only contract.

Suggested minimal fix shape
   switch (connectionState.status) {
@@
     case "no-app":
+      if (readOnly) {
+        return (
+          <GitHubSettingCard chevronState="disabled">
+            <SelectedConfig label="No repository connected" />
+          </GitHubSettingCard>
+        );
+      }
       return (
         <GitHubSettingCard chevronState="disabled">
@@
     case "no-repo":
+      if (readOnly) {
+        return (
+          <GitHubSettingCard chevronState="disabled">
+            <SelectedConfig label="No repository connected" />
+          </GitHubSettingCard>
+        );
+      }
       return <GitHubNoRepo projectId={projectId} installUrl={connectionState.installUrl} />;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx
around lines 55 - 75, When readOnly is true, ensure the "no-app" and "no-repo"
branches mirror the non-interactive behavior used in the "connected" branch: in
the "no-app" case render the ManageGitHubAppLink inside a GitHubSettingCard with
chevronState="disabled" and make the ManageGitHubAppLink non-interactive (either
pass a readOnly prop or use its "disabled"/outline variant without click
handlers), and in the "no-repo" case render GitHubNoRepo in a disabled state
(wrap it in GitHubSettingCard with chevronState="disabled" or pass readOnly to
GitHubNoRepo so it shows non-interactive UI). Target the case "no-app", case
"no-repo", the readOnly variable, and components GitHubSettingCard,
ManageGitHubAppLink, and GitHubNoRepo when applying the change.
🧹 Nitpick comments (2)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx (1)

16-16: Use a responsive container width.

Line 16 uses a fixed width (w-[900px]), which can overflow on narrower viewports. Prefer w-full max-w-[900px] (optionally with horizontal padding).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx
at line 16, The container uses a fixed className "w-[900px]" which can overflow
on small viewports; update the JSX div that currently has className="w-[900px]"
to use a responsive pattern such as "w-full max-w-[900px]" (optionally adding
horizontal padding like "px-4") so the container scales on narrow screens while
retaining the 900px maximum width.
web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx (1)

24-28: Consider using URLSearchParams for query parameters as a defensive best practice.

While the backend enforces strict slug format validation (lowercase letters, numbers, and hyphens only) at workspace creation, using URLSearchParams and explicit encoding remains a defensive programming best practice and improves readability:

Suggested refactor
const mutation = trpc.github.registerInstallation.useMutation({
  onSuccess: (data) => {
    toast.success("GitHub App installed");
+   const params = new URLSearchParams({
+     step: "select-repo",
+     projectId: data.projectId,
+   });
-   router.push(
-     `/${data.workspaceSlug}/projects/onboarding?step=select-repo&projectId=${data.projectId}`,
-   );
+   router.push(`/${data.workspaceSlug}/projects/onboarding?${params.toString()}`);
  },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/apps/dashboard/app/`(app)/integrations/github/callback/page.tsx around
lines 24 - 28, In onSuccess, build the redirect URL using URLSearchParams to
safely encode query parameters instead of string interpolation; replace the
router.push call that concatenates data.workspaceSlug and a query string with
assembling params via new URLSearchParams({ step: 'select-repo', projectId:
data.projectId }).toString() and then
router.push(`/${data.workspaceSlug}/projects/onboarding?${params}`), referencing
the onSuccess handler, router.push call, and data.workspaceSlug/data.projectId
to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/index.tsx:
- Line 60: The step with id "select-repo" currently uses the wrong label text;
update the StepWizard.Step that has id="select-repo" (the element with
label="Connect GitHub") to use the correct label, e.g., change label="Connect
GitHub" to label="Select repository" (or "Select Repository") so the wizard
navigation displays the repository selection step name properly.
- Around line 16-21: Validate and normalize URL-driven wizard state by parsing
searchParams into a typed/validated step and projectId before setting state:
replace direct use of initialStep/initialProjectId with a small parsing layer
that maps the raw step string to an allowed StepId union (e.g., "create-project"
| "configure-deployment" | ...) and treats unknown values as the default step;
ensure that when mapping to defaultStepId or when initializing projectId state
(projectId, setProjectId) you enforce that steps which require a projectId
(e.g., configure-deployment, review) are not selected if projectId is null —
instead fall back to "create-project" or another safe step and only set
projectId state when the parsed projectId is a valid non-empty string. Ensure
this validation is applied where defaultStepId is computed and at initialization
so illegal URL states can't render null content.

In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/page.tsx:
- Around line 11-13: The workspace lookup uses db.query.workspaces.findFirst and
only filters by orgId and deletedAtM, so it can return the wrong workspace for
this [workspaceSlug] route; update the where clause in the workspace query (the
call to db.query.workspaces.findFirst that assigns workspace) to also filter by
the route param slug (e.g., add and(eq(table.slug, workspaceSlug)) or
equivalent) so the record is scoped to the current workspaceSlug while keeping
the existing orgId and isNull(table.deletedAtM) checks.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx:
- Around line 19-21: The Deploy Button (the <Button> with type="submit") in this
component isn't wired to any handler or form target; either change it to a
regular button and add an onClick that calls a deploy handler (e.g.,
handleDeploy) or wrap the inputs in a <form> and implement an onSubmit handler
that performs the deploy action. Add a clearly named handler (handleDeploy or
onSubmitDeploy) inside the Configure Deployment component and wire it to the
Button via onClick or to the form via onSubmit, and ensure any required
props/state (deployment payload, loading state, navigation) are passed/updated
accordingly.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx:
- Around line 108-110: The dismiss button that renders <XMark> is missing an
accessible name; update the button (the element that calls
setIsBannerDismissed(true)) to provide an accessible label (e.g., add
aria-label="Dismiss" or aria-label="Close banner" or include visually hidden
text) so screen readers can announce its purpose while keeping the icon-only
visual; ensure the button still uses type="button" and retains its onClick
handler.
- Around line 76-93: The click handler handleSelectRepository awaits
selectRepoMutation.mutateAsync which can still reject after onError runs — wrap
the await in a try/catch so errors are handled instead of bubbling as unhandled
promise rejections: inside handleSelectRepository, keep
setMutatingRepoId(repo.id) and the finally that clears it, but change the try
block to try { await selectRepoMutation.mutateAsync(...); next(); } catch (err)
{ /* handle or log error; do not rethrow */ } finally { setMutatingRepoId(null);
} to ensure mutateAsync errors are caught; reference function
handleSelectRepository and method selectRepoMutation.mutateAsync.

---

Outside diff comments:
In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx:
- Around line 55-75: When readOnly is true, ensure the "no-app" and "no-repo"
branches mirror the non-interactive behavior used in the "connected" branch: in
the "no-app" case render the ManageGitHubAppLink inside a GitHubSettingCard with
chevronState="disabled" and make the ManageGitHubAppLink non-interactive (either
pass a readOnly prop or use its "disabled"/outline variant without click
handlers), and in the "no-repo" case render GitHubNoRepo in a disabled state
(wrap it in GitHubSettingCard with chevronState="disabled" or pass readOnly to
GitHubNoRepo so it shows non-interactive UI). Target the case "no-app", case
"no-repo", the readOnly variable, and components GitHubSettingCard,
ManageGitHubAppLink, and GitHubNoRepo when applying the change.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx:
- Around line 102-107: The Button currently uses disabled={disabled ||
isLoading} but still passes onClick={() => onSelect(repo)} which allows repeated
clicks during an in-flight selection; update the component to treat loading as a
hard interaction lock by including the component's loading prop in the disabled
condition (e.g., disabled when loading || isLoading || disabled) and add a
runtime guard at the start of the onClick handler (check loading || isLoading ||
disabled and return early) before calling onSelect(repo) so duplicate
submissions cannot be fired; locate the Button element and its onClick prop in
repo-list-item.tsx to apply these changes.

---

Nitpick comments:
In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx:
- Line 16: The container uses a fixed className "w-[900px]" which can overflow
on small viewports; update the JSX div that currently has className="w-[900px]"
to use a responsive pattern such as "w-full max-w-[900px]" (optionally adding
horizontal padding like "px-4") so the container scales on narrow screens while
retaining the 900px maximum width.

In `@web/apps/dashboard/app/`(app)/integrations/github/callback/page.tsx:
- Around line 24-28: In onSuccess, build the redirect URL using URLSearchParams
to safely encode query parameters instead of string interpolation; replace the
router.push call that concatenates data.workspaceSlug and a query string with
assembling params via new URLSearchParams({ step: 'select-repo', projectId:
data.projectId }).toString() and then
router.push(`/${data.workspaceSlug}/projects/onboarding?${params}`), referencing
the onSuccess handler, router.push call, and data.workspaceSlug/data.projectId
to locate the change.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6aee994 and 5f2e4c4.

📒 Files selected for processing (20)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/data-provider.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/page.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-links.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-step-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/page.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/connect-github.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/create-project.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/skeleton.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/projects-client.tsx
  • web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx
  • web/apps/dashboard/lib/trpc/routers/deploy/environment-settings/sentinel/update-middleware.ts
  • web/internal/ui/src/components/step-wizard/step-wizard.tsx
💤 Files with no reviewable changes (2)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx (2)

76-93: ⚠️ Potential issue | 🟠 Major

Catch mutateAsync rejection in handleSelectRepository.

Lines 82-89 can still reject and bubble from the click path; onError toast does not consume the rejected promise.

Suggested fix
   const handleSelectRepository = async (repo: {
     id: number;
     fullName: string;
     installationId: number;
   }) => {
     setMutatingRepoId(repo.id);
     try {
       await selectRepoMutation.mutateAsync({
         projectId,
         repositoryId: repo.id,
         repositoryFullName: repo.fullName,
         installationId: repo.installationId,
       });
       next();
+    } catch {
+      // Error toast is already handled in selectRepoMutation.onError
     } finally {
       setMutatingRepoId(null);
     }
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx
around lines 76 - 93, handleSelectRepository currently awaits
selectRepoMutation.mutateAsync which can reject and bubble up; wrap the await in
a try/catch so rejections are consumed (call next() only on success) and surface
an error toast in the catch instead of letting the promise reject, while keeping
the existing finally block to call setMutatingRepoId(null). Specifically update
the async body in handleSelectRepository to catch errors from
selectRepoMutation.mutateAsync, call the same onError/toast logic in the catch,
avoid rethrowing, and preserve calls to next() only after a successful mutate.

108-110: ⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only dismiss button.

Line 108 renders only an icon; provide an accessible name so screen readers can announce its purpose.

Suggested fix
-          <button type="button" onClick={() => setIsBannerDismissed(true)} className="ml-auto">
+          <button
+            type="button"
+            aria-label="Dismiss GitHub connected banner"
+            onClick={() => setIsBannerDismissed(true)}
+            className="ml-auto"
+          >
             <XMark iconSize="sm-regular" />
           </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx
around lines 108 - 110, The dismiss button currently renders only an icon
(XMark) and lacks an accessible name; update the button in the select-repo
component so screen readers can announce its purpose by adding an accessible
label (e.g., aria-label="Dismiss banner" or aria-label="Close") to the <button>
that calls setIsBannerDismissed(true), or include visually-hidden text inside
the button for the same effect while keeping the XMark icon.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/apps/dashboard/app/`(app)/integrations/github/callback/page.tsx:
- Around line 26-28: The callback navigation uses router.push which leaves the
callback URL in history and can cause the useEffect guard around the
registerInstallation mutation (the mutation created in page.tsx that calls
registerInstallation) to re-run on remount; replace the router.push(...) call
with router.replace(...) so the callback URL is not kept in history, preventing
back-button remounts from re-triggering the registerInstallation mutation and
eliminating duplicate backend calls.

---

Duplicate comments:
In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx:
- Around line 76-93: handleSelectRepository currently awaits
selectRepoMutation.mutateAsync which can reject and bubble up; wrap the await in
a try/catch so rejections are consumed (call next() only on success) and surface
an error toast in the catch instead of letting the promise reject, while keeping
the existing finally block to call setMutatingRepoId(null). Specifically update
the async body in handleSelectRepository to catch errors from
selectRepoMutation.mutateAsync, call the same onError/toast logic in the catch,
avoid rethrowing, and preserve calls to next() only after a successful mutate.
- Around line 108-110: The dismiss button currently renders only an icon (XMark)
and lacks an accessible name; update the button in the select-repo component so
screen readers can announce its purpose by adding an accessible label (e.g.,
aria-label="Dismiss banner" or aria-label="Close") to the <button> that calls
setIsBannerDismissed(true), or include visually-hidden text inside the button
for the same effect while keeping the XMark icon.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f2e4c4 and fa20f12.

📒 Files selected for processing (20)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/data-provider.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/page.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-links.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-step-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/page.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/connect-github.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/create-project.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/skeleton.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/projects-client.tsx
  • web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx
  • web/apps/dashboard/lib/trpc/routers/deploy/environment-settings/sentinel/update-middleware.ts
  • web/internal/ui/src/components/step-wizard/step-wizard.tsx
💤 Files with no reviewable changes (2)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/build-settings/github-settings/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/create-project.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/connect-github.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/data-provider.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/onboarding-step-header.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/configure-deployment.tsx
  • web/internal/ui/src/components/step-wizard/step-wizard.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/page.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/projects-client.tsx
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/steps/select-repo/repo-list-item.tsx
  • web/apps/dashboard/lib/trpc/routers/deploy/environment-settings/sentinel/update-middleware.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx (1)

20-29: ⚠️ Potential issue | 🟠 Major

Validate URL-driven wizard state at the boundary.

The step and projectId are read directly from query params without validation. A URL like ?step=configure-deployment without projectId will navigate to that step but render null content (lines 82-91). Per coding guidelines, inputs should be parsed at boundaries into typed structures.

🛡️ Suggested fix to validate step and guard navigation
+"use client";
+import { z } from "zod";
 import { collection } from "@/lib/collections";
 // ... other imports

+const StepId = z.enum([
+  "create-project",
+  "connect-github",
+  "select-repo",
+  "configure-deployment",
+]);
+type StepId = z.infer<typeof StepId>;

 export const Onboarding = () => {
   const existingProjects = useLiveQuery((q) => q.from({ project: collection.projects }), []);
   const isFirstProject = existingProjects.isLoading || existingProjects.data.length === 0;
   const searchParams = useSearchParams();

-  const initialStep = searchParams.get("step") ?? undefined;
-  const initialProjectId = searchParams.get("projectId") ?? undefined;
+  const parsedStep = StepId.safeParse(searchParams.get("step"));
+  const initialProjectId = searchParams.get("projectId") ?? undefined;
+
+  // Steps beyond create-project require a projectId
+  const stepsRequiringProjectId: StepId[] = ["connect-github", "select-repo", "configure-deployment"];
+  const requestedStep = parsedStep.success ? parsedStep.data : undefined;
+  const canStartAtStep =
+    !requestedStep ||
+    requestedStep === "create-project" ||
+    Boolean(initialProjectId);
+  const initialStep = canStartAtStep ? requestedStep : "create-project";

   const [projectId, setProjectId] = useState<string | null>(initialProjectId ?? null);

   return (
     <div>
-      <StepWizard.Root defaultStepId={initialStep}>
+      <StepWizard.Root defaultStepId={initialStep}>

As per coding guidelines: "Make illegal states unrepresentable by modeling domain with discriminated unions and parsing inputs at boundaries into typed structures."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/index.tsx
around lines 20 - 29, Parse and validate the query params at the boundary:
convert initialStep and initialProjectId into typed values (e.g., a Step
discriminated union and a nullable ProjectId) before calling useState; ensure
initialStep only becomes a valid step id for StepWizard.Root (reject unknown
strings) and if a step that requires projectId (the ones used in the render
logic that currently produce null) is requested without a valid
initialProjectId, override to a safe default step (or clear to undefined) so
projectId/useState and StepWizard.Root never enter an invalid state; update the
initialization of projectId/setProjectId and defaultStepId to use these
parsed/validated values and centralize this parsing adjacent to where
initialStep/initialProjectId are read.
🧹 Nitpick comments (1)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx (1)

77-77: Remove trailing whitespace after JSX element.

There's an extra space after the <SelectRepo /> component that appears to be unintentional.

✨ Suggested fix
-              <SelectRepo projectId={projectId} />{" "}
+              <SelectRepo projectId={projectId} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/index.tsx
at line 77, The JSX line rendering SelectRepo contains an unintended trailing
whitespace token; remove the extra {" "} or any trailing space after the
<SelectRepo projectId={projectId} /> component so the element is not followed by
stray whitespace in the render output (update the JSX in the onboarding
component where SelectRepo is used).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/index.tsx:
- Around line 20-29: Parse and validate the query params at the boundary:
convert initialStep and initialProjectId into typed values (e.g., a Step
discriminated union and a nullable ProjectId) before calling useState; ensure
initialStep only becomes a valid step id for StepWizard.Root (reject unknown
strings) and if a step that requires projectId (the ones used in the render
logic that currently produce null) is requested without a valid
initialProjectId, override to a safe default step (or clear to undefined) so
projectId/useState and StepWizard.Root never enter an invalid state; update the
initialization of projectId/setProjectId and defaultStepId to use these
parsed/validated values and centralize this parsing adjacent to where
initialStep/initialProjectId are read.

---

Nitpick comments:
In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/onboarding/index.tsx:
- Line 77: The JSX line rendering SelectRepo contains an unintended trailing
whitespace token; remove the extra {" "} or any trailing space after the
<SelectRepo projectId={projectId} /> component so the element is not followed by
stray whitespace in the render output (update the JSX in the onboarding
component where SelectRepo is used).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54f7861 and 39e0808.

📒 Files selected for processing (2)
  • web/apps/dashboard/app/(app)/[workspaceSlug]/projects/onboarding/index.tsx
  • web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx

@chronark chronark merged commit ab63f35 into main Mar 3, 2026
14 checks passed
@chronark chronark deleted the feat/next-onboarding-step branch March 3, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants