Skip to content

Comments

Feature(webapp): new User and Project onboarding questions#3109

Open
samejr wants to merge 18 commits intomainfrom
feature(webapp)-new-onboarding-questions
Open

Feature(webapp): new User and Project onboarding questions#3109
samejr wants to merge 18 commits intomainfrom
feature(webapp)-new-onboarding-questions

Conversation

@samejr
Copy link
Member

@samejr samejr commented Feb 21, 2026

  • New User onboarding questions added and stored in a new onboardingData col
  • Keeps the same Org creation screen and stores the data in the same format in same DB column
  • New Org onboarding questions addded and stored in a new onboardingData col
New.onboarding.demo.mp4

@changeset-bot
Copy link

changeset-bot bot commented Feb 21, 2026

⚠️ No Changeset found

Latest commit: 013a110

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new TechnologyPicker React component (searchable multi-select with custom values) and a CheckboxIndicator primitive; updates Select primitive to support left/right check indicators and adjust trigger layout. Integrates TechnologyPicker and additional onboarding fields (workingOn, technologies, goals, referralSource, role) into project/user onboarding UI and form handling. Extends server-side models to accept and persist onboardingData, adds onboardingData Json? fields to Prisma schema for User and Project, and includes a SQL migration to add JSONB onboardingData columns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete; it omits required template sections including Testing, Changelog, Screenshots, and the Checklist. Only a brief summary and asset reference are provided. Complete the pull request template by adding Testing steps, a detailed Changelog, Screenshots if applicable, and the required Checklist with completed items.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main objective: adding new onboarding questions for both User and Project with storage in a new database column.

✏️ 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 feature(webapp)-new-onboarding-questions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

coderabbitai[bot]

This comment was marked as resolved.

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: 2

🧹 Nitpick comments (4)
apps/webapp/app/components/primitives/CheckboxIndicator.tsx (1)

12-20: Add aria-hidden="true" to the decorative SVG.

The SVG has no accessible name. Since CheckboxIndicator is always placed inside an Ariakit SelectItem/ComboboxItem that already communicates selection state via aria-selected, the graphic is purely decorative and should be hidden from the accessibility tree to avoid spurious announcements.

♿️ Proposed fix
-        <svg className="size-3 text-white" viewBox="0 0 12 12" fill="none">
+        <svg className="size-3 text-white" viewBox="0 0 12 12" fill="none" aria-hidden="true">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/components/primitives/CheckboxIndicator.tsx` around lines 12
- 20, The SVG inside the CheckboxIndicator component is decorative and lacks an
accessible name; update the SVG element in CheckboxIndicator to include
aria-hidden="true" so the graphic is removed from the accessibility tree (since
selection state is already conveyed by the surrounding SelectItem/ComboboxItem
via aria-selected).
apps/webapp/app/components/primitives/Select.tsx (3)

443-448: interface SelectItemPropstype SelectItemProps per coding guidelines.

The checkPosition prop is being added to this declaration in this PR, making it a touched symbol. Per the coding guidelines, TypeScript interface should be replaced with type.

♻️ Proposed fix
-export interface SelectItemProps extends Ariakit.SelectItemProps {
+export type SelectItemProps = Ariakit.SelectItemProps & {
   icon?: React.ReactNode;
   checkIcon?: React.ReactNode;
   checkPosition?: "left" | "right";
   shortcut?: ShortcutDefinition;
-}
+};

As per coding guidelines: "Use types over interfaces for TypeScript."

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

In `@apps/webapp/app/components/primitives/Select.tsx` around lines 443 - 448,
Replace the touched symbol declaration from an interface to a type: change the
declaration of SelectItemProps so it uses a TypeScript type alias instead of an
interface (e.g., make SelectItemProps a type that composes/extends
Ariakit.SelectItemProps via an intersection) and preserve all existing fields
(icon, checkIcon, checkPosition, shortcut) and their exact types; update any
related usages or imports if necessary to reference the new type alias name.

453-453: Move the import to the top of the file.

Placing an import in the middle of the file (after constants and before a function) is non-standard and can confuse tooling and readers alike.

♻️ Proposed fix

Move line 453 to join the other imports at the top of the file (after line 11):

+import { CheckboxIndicator } from "./CheckboxIndicator";
 import { ShortcutKey } from "./ShortcutKey";
 import { ChevronDown } from "lucide-react";
 import { type MatchSorterOptions, matchSorter } from "match-sorter";

And remove the import from line 453.

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

In `@apps/webapp/app/components/primitives/Select.tsx` at line 453, The import for
CheckboxIndicator is placed mid-file; move the import statement for
CheckboxIndicator into the module's top import block alongside the other imports
(so CheckboxIndicator is imported with the other dependencies), and remove the
duplicate/mid-file import so only the top-level import remains.

465-472: select?.useState("value") subscribes every SelectItem to value changes — consider scoping to left-check items only.

store.useState(key) causes all SelectItem elements to re-render whenever that state key changes, even when the change is unrelated to a specific item; Ariakit already optimizes its own internal subscriptions, so external store subscriptions should be added carefully.

Multiple useSyncExternalStore calls in composite item components are Ariakit's identified performance bottleneck — each composite item may call uSES over 10 times, and React performs extra work with each such call.

Adding select?.useState("value") to SelectItem means every item (including those with checkPosition === "right", the default) subscribes to the global selection value and re-renders on every toggle. With the TechnologyPicker rendering 60+ ComboboxItem instances, this multiplies the re-render work per selection.

The fix is to extract the left-side indicator subscription into a small child component so only items that opt into checkPosition="left" bear the subscription cost:

♻️ Proposed refactor
+function LeftCheckIndicator({ value }: { value?: string }) {
+  const select = Ariakit.useSelectContext();
+  const selectValue = select?.useState("value");
+  const isChecked = React.useMemo(() => {
+    if (!value || selectValue == null) return false;
+    if (Array.isArray(selectValue)) return selectValue.includes(value);
+    return selectValue === value;
+  }, [selectValue, value]);
+  return <CheckboxIndicator checked={isChecked} />;
+}

 export function SelectItem({
   icon,
   checkIcon = <Ariakit.SelectItemCheck className="size-8 flex-none text-text-bright" />,
   checkPosition = "right",
   shortcut,
   ...props
 }: SelectItemProps) {
   const combobox = Ariakit.useComboboxContext();
   const render = combobox ? <Ariakit.ComboboxItem render={props.render} /> : undefined;
   const ref = React.useRef<HTMLDivElement>(null);
-  const select = Ariakit.useSelectContext();
-  const selectValue = select?.useState("value");
-
-  const isChecked = React.useMemo(() => {
-    if (!props.value || selectValue == null) return false;
-    if (Array.isArray(selectValue)) return selectValue.includes(props.value);
-    return selectValue === props.value;
-  }, [selectValue, props.value]);
   // ...
       <div className={cn(...)}>
-        {checkPosition === "left" && <CheckboxIndicator checked={isChecked} />}
+        {checkPosition === "left" && <LeftCheckIndicator value={props.value} />}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/components/primitives/Select.tsx` around lines 465 - 472,
SelectItem currently calls select?.useState("value") (via selectValue) causing
every item to subscribe and re-render; move that subscription into a tiny child
component (e.g., LeftCheckIndicator) and only render it when props.checkPosition
=== "left". Concretely: remove select?.useState("value") and the
selectValue-based isChecked computation from SelectItem, create a
LeftCheckIndicator component that calls select?.useState("value"), computes
whether the item is checked (mirroring the current Array.isArray/equals logic),
and renders the left-side indicator; in SelectItem render LeftCheckIndicator
only when props.checkPosition === "left" so right-positioned items no longer
subscribe to the select store.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26d9178 and c82e500.

📒 Files selected for processing (4)
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/components/primitives/Select.tsx
  • apps/webapp/app/models/project.server.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/models/project.server.ts
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/models/project.server.ts
🧠 Learnings (5)
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/components/primitives/CheckboxIndicator.tsx
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/models/project.server.ts
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.

Applied to files:

  • apps/webapp/app/models/project.server.ts
📚 Learning: 2026-01-28T16:57:47.620Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 2964
File: apps/webapp/app/components/AskAI.tsx:121-141
Timestamp: 2026-01-28T16:57:47.620Z
Learning: In the trigger.dev webapp codebase, the Button component (apps/webapp/app/components/primitives/Buttons) does not spread unknown props to the underlying <button> element—it only passes specific props (type, disabled, onClick, name, value, ref, form, autoFocus). When using Radix UI's TooltipTrigger with asChild, a span wrapper around the Button is necessary to receive Radix props (aria-describedby, onPointerEnter, onPointerLeave, data-state) while the Button handles its own behavior. Directly making the Button the child of TooltipTrigger with asChild will break accessibility and tooltip functionality.

Applied to files:

  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/components/primitives/Select.tsx
📚 Learning: 2026-02-03T18:27:49.039Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:49.039Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (like the Edit button with PencilSquareIcon) intentionally have no text labels - only icons are shown in the TableCellMenu. This is a deliberate UI design pattern for compact icon-only menu items.

Applied to files:

  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
🧬 Code graph analysis (3)
apps/webapp/app/components/primitives/CheckboxIndicator.tsx (1)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/onboarding/TechnologyPicker.tsx (3)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/CheckboxIndicator.tsx (1)
  • CheckboxIndicator (3-24)
apps/webapp/app/components/primitives/ShortcutKey.tsx (1)
  • ShortcutKey (41-58)
apps/webapp/app/components/primitives/Select.tsx (2)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/CheckboxIndicator.tsx (1)
  • CheckboxIndicator (3-24)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Bun Runtime
🔇 Additional comments (2)
apps/webapp/app/models/project.server.ts (2)

4-5: Import changes look clean.

Switching to import type { Prisma, Project } and using the inline type modifier on Organization correctly avoids importing runtime values that are used only in type positions.


82-92: LGTM — onboardingData is correctly threaded through retries and persisted.

The recursive slug-collision retry path (lines 82–92) forwards onboardingData unchanged, and the Prisma create call (line 105) passes it directly. Prisma treats undefined as "omit the field", so when onboardingData is not supplied the column will receive its schema default rather than an explicit write.

Also applies to: 105-105

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/webapp/app/components/onboarding/TechnologyPicker.tsx`:
- Around line 262-264: The "No options" JSX branch is unreachable because
filteredOptions returns the constant TECHNOLOGY_OPTIONS when !searchValue and
TECHNOLOGY_OPTIONS is always non-empty; update TechnologyPicker to either remove
the dead JSX (the conditional rendering that checks filteredOptions.length === 0
&& !searchValue) or make the component actually support a dynamic options prop
used by filteredOptions (replace hard-coded TECHNOLOGY_OPTIONS usage inside the
filteredOptions logic with the incoming options prop and validate that options
can be empty), then keep the guard. Locate references to filteredOptions,
searchValue, and TECHNOLOGY_OPTIONS to apply the change.

In `@apps/webapp/app/models/project.server.ts`:
- Line 17: Add a Zod schema to validate the constructed onboardingData object
before calling createProject: define a Zod object schema (e.g.,
onboardingDataSchema) that enforces the expected keys and types (each array
field is z.array(z.string()).max/min as appropriate and any scalar fields
typed), import and use it in the route that builds onboardingData (the route
that parses projectName/projectVersion and uses JSON.parse at lines producing
string[] casts), run onboardingDataSchema.parse(parsedObject) and pass the
resulting validated object to createProject; also tighten the onboardingData?:
Prisma.InputJsonValue type in project.server.ts if needed to reflect the
validated shape.

---

Duplicate comments:
In `@apps/webapp/app/components/onboarding/TechnologyPicker.tsx`:
- Around line 186-192: The remove button in TechnologyPicker (the <button>
containing <XMarkIcon> and calling removeItem(item)) has no accessible name;
update the button to provide an accessible label (for example set
aria-label={`Remove ${item}`} or aria-labelledby tieing to a visually hidden
span containing "Remove {item}") so screen readers can identify the target and
action; ensure the label uses the same item variable passed to removeItem and
keep the existing onClick handler and XMarkIcon for visual UI.
- Around line 215-217: The onClick handler on the Ariakit.Select trigger
(onClick={() => setOpen(true)}) overrides Ariakit's internal close behavior
managed by SelectProvider, preventing the popover from closing when the trigger
is clicked; remove this explicit onClick or replace it with a non-interfering
toggle (use the provided state setter pattern like setOpen(prev => !prev) only
if you must control toggle behavior), so that Ariakit.Select's built-in click
handler can properly call setOpen(false) and close the popover—look for the
Ariakit.Select element and the setOpen usage to update accordingly.
- Line 283: In TechnologyPicker (component/file TechnologyPicker.tsx) the
className string contains a typo "pl-0.5can" which is not a valid Tailwind
class; change that token to "pl-0.5" so the padding applies correctly and leave
the rest of the className unchanged (locate the string passed to className on
the input/element with "pl-0.5can flex-1 border-none..." and replace only the
invalid token).

---

Nitpick comments:
In `@apps/webapp/app/components/primitives/CheckboxIndicator.tsx`:
- Around line 12-20: The SVG inside the CheckboxIndicator component is
decorative and lacks an accessible name; update the SVG element in
CheckboxIndicator to include aria-hidden="true" so the graphic is removed from
the accessibility tree (since selection state is already conveyed by the
surrounding SelectItem/ComboboxItem via aria-selected).

In `@apps/webapp/app/components/primitives/Select.tsx`:
- Around line 443-448: Replace the touched symbol declaration from an interface
to a type: change the declaration of SelectItemProps so it uses a TypeScript
type alias instead of an interface (e.g., make SelectItemProps a type that
composes/extends Ariakit.SelectItemProps via an intersection) and preserve all
existing fields (icon, checkIcon, checkPosition, shortcut) and their exact
types; update any related usages or imports if necessary to reference the new
type alias name.
- Line 453: The import for CheckboxIndicator is placed mid-file; move the import
statement for CheckboxIndicator into the module's top import block alongside the
other imports (so CheckboxIndicator is imported with the other dependencies),
and remove the duplicate/mid-file import so only the top-level import remains.
- Around line 465-472: SelectItem currently calls select?.useState("value") (via
selectValue) causing every item to subscribe and re-render; move that
subscription into a tiny child component (e.g., LeftCheckIndicator) and only
render it when props.checkPosition === "left". Concretely: remove
select?.useState("value") and the selectValue-based isChecked computation from
SelectItem, create a LeftCheckIndicator component that calls
select?.useState("value"), computes whether the item is checked (mirroring the
current Array.isArray/equals logic), and renders the left-side indicator; in
SelectItem render LeftCheckIndicator only when props.checkPosition === "left" so
right-positioned items no longer subscribe to the select store.

Comment on lines +262 to +264
{filteredOptions.length === 0 && !searchValue && (
<div className="px-3 py-2 text-xs text-text-dimmed">No options</div>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

"No options" branch is unreachable dead code.

filteredOptions returns TECHNOLOGY_OPTIONS directly when !searchValue (line 128), and TECHNOLOGY_OPTIONS is always non-empty (62 entries). The condition filteredOptions.length === 0 && !searchValue can therefore never be true. If this message is intended as a future-proofing guard for a dynamic options prop, that intent should be reflected in the component's API; otherwise, remove it.

🐛 Proposed fix
-              {filteredOptions.length === 0 && !searchValue && (
-                <div className="px-3 py-2 text-xs text-text-dimmed">No options</div>
-              )}
-
               {filteredOptions.length === 0 && searchValue && (
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{filteredOptions.length === 0 && !searchValue && (
<div className="px-3 py-2 text-xs text-text-dimmed">No options</div>
)}
{filteredOptions.length === 0 && searchValue && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/components/onboarding/TechnologyPicker.tsx` around lines 262
- 264, The "No options" JSX branch is unreachable because filteredOptions
returns the constant TECHNOLOGY_OPTIONS when !searchValue and TECHNOLOGY_OPTIONS
is always non-empty; update TechnologyPicker to either remove the dead JSX (the
conditional rendering that checks filteredOptions.length === 0 && !searchValue)
or make the component actually support a dynamic options prop used by
filteredOptions (replace hard-coded TECHNOLOGY_OPTIONS usage inside the
filteredOptions logic with the incoming options prop and validate that options
can be empty), then keep the guard. Locate references to filteredOptions,
searchValue, and TECHNOLOGY_OPTIONS to apply the change.

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: 2

🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)

161-169: Schema fields carry opaque z.string() for JSON-encoded arrays — consider documenting the contract.

workingOn, technologies, technologiesOther, and goals are z.string().optional() in the form schema but are expected to contain JSON-encoded string arrays. This isn't obvious from the schema alone. A brief inline comment (e.g., // JSON-encoded string[]) would help future maintainers understand why the action calls JSON.parse on these values.

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

In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx
around lines 161 - 169, The schema's string fields (schema and its fields
projectName, projectVersion, workingOn, workingOnOther, technologies,
technologiesOther, goals) hide the fact that workingOn, technologies,
technologiesOther and goals are expected to be JSON-encoded string[]; update the
declaration in route.tsx by adding short inline comments next to those fields
(e.g., // JSON-encoded string[]) and optionally add a z.preprocess or refine
note if you want runtime validation to assert they parse to arrays so future
maintainers see the contract clearly when inspecting the schema or action that
calls JSON.parse.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c82e500 and c15dbc9.

📒 Files selected for processing (3)
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
  • apps/webapp/app/routes/confirm-basic-details.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/app/components/onboarding/TechnologyPicker.tsx
  • apps/webapp/app/routes/confirm-basic-details.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
🧠 Learnings (10)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (4)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (4)

40-67: LGTM — constants and shuffle utility look good.

WORKING_ON_OTHER properly centralizes the magic string (addressing a prior review). The Fisher-Yates shuffle is correctly implemented, and as const tuples provide good type narrowing for the option arrays.


69-108: LGTM — clean reusable multi-select wrapper.

The MultiSelectField component cleanly encapsulates the Select multi-value pattern with a nice truncated display. The text callback gracefully handles the empty-selection case.


298-310: LGTM — Page setup and form initialization.

Standard Remix pattern with useTypedLoaderData, conform useForm, and navigation state. No issues.


364-420: Hidden inputs always submit "[]" when nothing is selected — harmless but worth noting.

JSON.stringify([]) yields "[]", which is truthy and enters the action's if (submission.value.workingOn) branch. The downstream length check (workingOn.length > 0) prevents it from being stored, so this is functionally correct. Just noting the implicit reliance on the length guard for correctness.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx:
- Around line 315-326: The flicker happens because shuffledWorkingOn is
initialized client-side before the useEffect shuffle runs; to avoid the brief
visual reorder, add a boolean state (e.g., hasShuffled) initialized false,
change shuffledWorkingOn initial value to [] (or keep but hide UI), then in the
existing useEffect that calls shuffleArray(nonOther) also setHasShuffled(true);
update the component rendering of the dropdown/options to not render (or render
a stable placeholder) until hasShuffled is true; reference shuffledWorkingOn,
useEffect, shuffleArray, WORKING_ON_OTHER and setShuffledWorkingOn when making
these changes.
- Around line 188-227: Parsing of optional onboarding fields
(submission.value.workingOn, technologies, goals, technologiesOther) currently
throws and aborts createProject; wrap parsing in a small helper that safely
parses JSON and validates with zod without throwing (e.g.,
parseOptionalStringArray(value): if no value return undefined; try JSON.parse
then call stringArraySchema.safeParse and return the parsed array on success or
undefined on failure), use that helper when building onboardingData so malformed
hidden inputs are ignored rather than propagating errors to createProject, and
keep the createProject call unchanged (reference: stringArraySchema,
onboardingData, createProject, and submission.value.*).

---

Nitpick comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx:
- Around line 161-169: The schema's string fields (schema and its fields
projectName, projectVersion, workingOn, workingOnOther, technologies,
technologiesOther, goals) hide the fact that workingOn, technologies,
technologiesOther and goals are expected to be JSON-encoded string[]; update the
declaration in route.tsx by adding short inline comments next to those fields
(e.g., // JSON-encoded string[]) and optionally add a z.preprocess or refine
note if you want runtime validation to assert they parse to arrays so future
maintainers see the contract clearly when inspecting the schema or action that
calls JSON.parse.

Comment on lines +315 to +326
const [selectedWorkingOn, setSelectedWorkingOn] = useState<string[]>([]);
const [workingOnOther, setWorkingOnOther] = useState("");
const [selectedTechnologies, setSelectedTechnologies] = useState<string[]>([]);
const [customTechnologies, setCustomTechnologies] = useState<string[]>([]);
const [selectedGoals, setSelectedGoals] = useState<string[]>([]);

const [shuffledWorkingOn, setShuffledWorkingOn] = useState<string[]>([...workingOnOptions]);

useEffect(() => {
const nonOther = workingOnOptions.filter((o) => o !== WORKING_ON_OTHER);
setShuffledWorkingOn([...shuffleArray(nonOther), WORKING_ON_OTHER]);
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Shuffle runs client-side only — expect a brief visual reorder after hydration.

shuffledWorkingOn initialises with the static order (line 321), then useEffect fires post-hydration to shuffle (line 323-326). This means users will momentarily see options in the original order before they jump to the shuffled order.

If this flicker is acceptable for bias reduction, consider adding a short comment to document the intent. If not, you could hide the dropdown (or its items) until the effect has run.

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

In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx
around lines 315 - 326, The flicker happens because shuffledWorkingOn is
initialized client-side before the useEffect shuffle runs; to avoid the brief
visual reorder, add a boolean state (e.g., hasShuffled) initialized false,
change shuffledWorkingOn initial value to [] (or keep but hide UI), then in the
existing useEffect that calls shuffleArray(nonOther) also setHasShuffled(true);
update the component rendering of the dropdown/options to not render (or render
a stable placeholder) until hasShuffled is true; reference shuffledWorkingOn,
useEffect, shuffleArray, WORKING_ON_OTHER and setShuffledWorkingOn when making
these changes.

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.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c15dbc9 and 934c178.

📒 Files selected for processing (1)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
🧠 Learnings (10)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
🧬 Code graph analysis (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (2)
apps/webapp/app/components/primitives/Select.tsx (1)
  • Select (112-258)
apps/webapp/app/components/onboarding/TechnologyPicker.tsx (1)
  • TechnologyPicker (113-324)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (6)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (6)

40-67: LGTM — constants and shuffle utility are clean.

WORKING_ON_OTHER as a single source of truth for the magic string addresses the prior review feedback. shuffleArray is a correct Fisher-Yates shuffle, and the as const tuples are idiomatic.


69-108: LGTM — MultiSelectField is a clean, focused helper.

Good use of an inline type for props and a function declaration. The text render prop gracefully handles the empty and overflow cases.


161-169: LGTM — schema extension is appropriate.

The optional string fields correctly model the serialized JSON hidden inputs. Deeper validation happens in the action via safeParseStringArray.


188-224: Well-implemented safe parsing — addresses all prior feedback.

safeParseStringArray correctly isolates JSON parsing and Zod validation failures so they never abort project creation. The helper eliminates the previous code duplication and unsafe as string[] casts.


361-417: LGTM — onboarding form fields are well-wired.

Hidden inputs correctly serialize the component state as JSON, and the conditional rendering of workingOnOther (Line 372–385) properly gates on the WORKING_ON_OTHER selection. The TechnologyPicker integration is clean.


345-347: Nice UX touch with the required asterisk.

Clear visual distinction between the required project name and the optional onboarding fields below the divider.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx:
- Around line 318-323: The hydration reorder concern can be eliminated by
computing the shuffled options synchronously during render instead of in
useEffect: replace the useEffect + initial useState([...workingOnOptions])
pattern by initializing shuffledWorkingOn with a lazy initializer that builds
nonOther = workingOnOptions.filter(o => o !== WORKING_ON_OTHER) and returns
[...shuffleArray(nonOther), WORKING_ON_OTHER]; remove the empty useEffect and
the setShuffledWorkingOn call, keeping references to shuffledWorkingOn,
setShuffledWorkingOn, workingOnOptions, shuffleArray, and WORKING_ON_OTHER to
locate the change.

@samejr samejr marked this pull request as ready for review February 21, 2026 16:52
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Choose a reason for hiding this comment

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

🚩 Removal of sendNewOrgMessage (Slack notification) in org creation

The old org creation route sent a Slack message via sendNewOrgMessage when a user provided a whyUseUs answer (_app.orgs.new/route.tsx old lines 62-69). This entire feature has been removed along with the whyUseUs field. The new onboarding data is stored in the database but there's no replacement notification mechanism. This may be intentional (shifting from Slack notifications to database queries for analytics), but the team should confirm they don't need real-time alerts for new org signups.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Member Author

Choose a reason for hiding this comment

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

Intentional. It will be replaced with a better slack notification later.

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.

🧹 Nitpick comments (3)
apps/webapp/app/routes/confirm-basic-details.tsx (3)

5-5: RadioGroup is imported directly from Radix, bypassing the local primitive wrapper.

RadioGroupItem comes from ~/components/primitives/RadioButton, which also exports a RadioGroup wrapper around RadioGroupPrimitive.Root. Importing one component from the primitive and the other directly from @radix-ui/react-radio-group creates an inconsistent import pattern with no functional benefit.

♻️ Proposed fix
-import { RadioGroup } from "@radix-ui/react-radio-group";
 ...
-import { RadioGroupItem } from "~/components/primitives/RadioButton";
+import { RadioGroup, RadioGroupItem } from "~/components/primitives/RadioButton";

Also applies to: 22-22

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

In `@apps/webapp/app/routes/confirm-basic-details.tsx` at line 5, The file
inconsistently imports RadioGroup from "@radix-ui/react-radio-group" while using
RadioGroupItem from the local primitive; replace the direct Radix import with
the local wrapper export so both come from the same primitive module (use the
RadioGroup export from ~/components/primitives/RadioButton alongside
RadioGroupItem) to keep imports consistent and avoid mixing Radix primitives
with the app's wrapper component.

204-205: Inconsistent initial values for the two controlled selection states.

selectedReferralSource starts as undefined while selectedRole starts as "". Both are treated the same way downstream (falsy check in the action, and the text prop guard on the Select), but the differing conventions add minor cognitive overhead. Using undefined for both (to represent "nothing selected") would be more uniform.

♻️ Proposed fix
-  const [selectedRole, setSelectedRole] = useState<string>("");
+  const [selectedRole, setSelectedRole] = useState<string | undefined>(undefined);

The text prop already handles undefined correctly (v ? ... : undefined), and the hidden input's value attribute can mirror the pattern already used for referral source:

-                    <input type="hidden" name="role" value={selectedRole} />
+                    <input type="hidden" name="role" value={selectedRole ?? ""} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/routes/confirm-basic-details.tsx` around lines 204 - 205, The
two controlled selection states use inconsistent empty
values—selectedReferralSource is initialized to undefined while selectedRole is
initialized to ""—so change selectedRole to use undefined as its initial state
to match selectedReferralSource; update the initialization of selectedRole
(useState for selectedRole) to use undefined, ensure the Select component
continues to rely on the existing text prop guard (v ? ... : undefined), and
mirror this pattern for the hidden input's value so both referral source and
role treat "nothing selected" uniformly.

140-140: Record<string, string | undefined> is unnecessarily permissive given actual usage.

All values assigned to onboardingData are concrete strings. The type declaration allows undefined as a value, but no code assigns undefined to any property. Prisma 6.14.0's InputJsonValue type fully supports Record<string, string>.

♻️ Proposed fix
-    const onboardingData: Record<string, string | undefined> = {};
+    const onboardingData: Record<string, string> = {};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/routes/confirm-basic-details.tsx` at line 140, The variable
onboardingData is typed as Record<string, string | undefined> but all assigned
values are concrete strings, so tighten the type to Record<string, string> (or
Prisma's InputJsonValue-compatible type if preferred) to avoid allowing
undefined; update the onboardingData declaration (the onboardingData variable in
confirm-basic-details.tsx) to use Record<string, string> and adjust any
downstream consumers if they expect optional values.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 934c178 and 013a110.

📒 Files selected for processing (1)
  • apps/webapp/app/routes/confirm-basic-details.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
🧠 Learnings (9)
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2026-01-28T16:57:47.620Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 2964
File: apps/webapp/app/components/AskAI.tsx:121-141
Timestamp: 2026-01-28T16:57:47.620Z
Learning: In the trigger.dev webapp codebase, the Button component (apps/webapp/app/components/primitives/Buttons) does not spread unknown props to the underlying <button> element—it only passes specific props (type, disabled, onClick, name, value, ref, form, autoFocus). When using Radix UI's TooltipTrigger with asChild, a span wrapper around the Button is necessary to receive Radix props (aria-describedby, onPointerEnter, onPointerLeave, data-state) while the Button handles its own behavior. Directly making the Button the child of TooltipTrigger with asChild will break accessibility and tooltip functionality.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2025-08-14T10:09:02.528Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: internal-packages/run-engine/src/engine/index.ts:466-467
Timestamp: 2025-08-14T10:09:02.528Z
Learning: In the triggerdotdev/trigger.dev codebase, it's acceptable to pass `string | undefined` types directly to Prisma operations (both create and update). The codebase consistently uses this pattern and the team is comfortable with how Prisma handles undefined values.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2025-09-05T15:32:41.553Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/services/projectSettings.server.ts:210-0
Timestamp: 2025-09-05T15:32:41.553Z
Learning: In the ProjectSettingsService.updateGitSettings method in apps/webapp/app/services/projectSettings.server.ts, when productionBranch or stagingBranch parameters are undefined, the method intentionally writes empty objects to clear existing branch configurations. This is the desired behavior, not a bug.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2025-05-12T17:23:05.574Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2035
File: apps/webapp/app/v3/services/finalizeTaskRun.server.ts:99-103
Timestamp: 2025-05-12T17:23:05.574Z
Learning: Prisma ignores undefined values in update operations. When a field is set to undefined in a Prisma update operation's data object, Prisma will omit that field from the update rather than trying to set it to null or another value. This means there's no need to conditionally build update objects to exclude undefined values.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2025-05-12T17:23:05.574Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2035
File: apps/webapp/app/v3/services/finalizeTaskRun.server.ts:99-103
Timestamp: 2025-05-12T17:23:05.574Z
Learning: Prisma ignores undefined values in update operations. When a field has the value `undefined` in a Prisma update operation's data object, Prisma will automatically exclude that field from the update query rather than trying to set it to null. This means there's no need to manually build update objects to filter out undefined values.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/routes/confirm-basic-details.tsx
🧬 Code graph analysis (1)
apps/webapp/app/routes/confirm-basic-details.tsx (6)
apps/webapp/app/models/user.server.ts (1)
  • updateUser (329-345)
apps/webapp/app/components/layout/AppLayout.tsx (1)
  • MainCenteredContainer (50-64)
apps/webapp/app/components/primitives/Label.tsx (1)
  • Label (25-41)
apps/webapp/app/components/primitives/Input.tsx (1)
  • Input (125-125)
apps/webapp/app/components/primitives/RadioButton.tsx (2)
  • RadioGroup (93-98)
  • RadioGroupItem (112-165)
apps/webapp/app/components/primitives/Select.tsx (2)
  • Select (112-258)
  • SelectItem (455-519)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Bun Runtime
🔇 Additional comments (1)
apps/webapp/app/routes/confirm-basic-details.tsx (1)

308-335: Accessibility improvements look good.

Both aria-labelledby="referral-label" on the RadioGroup (line 324) and aria-labelledby="role-label" on the Select (line 355) correctly address the past review comments about unlabelled controls.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/webapp/app/routes/confirm-basic-details.tsx`:
- Line 5: The file inconsistently imports RadioGroup from
"@radix-ui/react-radio-group" while using RadioGroupItem from the local
primitive; replace the direct Radix import with the local wrapper export so both
come from the same primitive module (use the RadioGroup export from
~/components/primitives/RadioButton alongside RadioGroupItem) to keep imports
consistent and avoid mixing Radix primitives with the app's wrapper component.
- Around line 204-205: The two controlled selection states use inconsistent
empty values—selectedReferralSource is initialized to undefined while
selectedRole is initialized to ""—so change selectedRole to use undefined as its
initial state to match selectedReferralSource; update the initialization of
selectedRole (useState for selectedRole) to use undefined, ensure the Select
component continues to rely on the existing text prop guard (v ? ... :
undefined), and mirror this pattern for the hidden input's value so both
referral source and role treat "nothing selected" uniformly.
- Line 140: The variable onboardingData is typed as Record<string, string |
undefined> but all assigned values are concrete strings, so tighten the type to
Record<string, string> (or Prisma's InputJsonValue-compatible type if preferred)
to avoid allowing undefined; update the onboardingData declaration (the
onboardingData variable in confirm-basic-details.tsx) to use Record<string,
string> and adjust any downstream consumers if they expect optional values.

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.

1 participant