Feature(webapp): new User and Project onboarding questions#3109
Feature(webapp): new User and Project onboarding questions#3109
Conversation
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds 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)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
apps/webapp/app/components/primitives/CheckboxIndicator.tsx (1)
12-20: Addaria-hidden="true"to the decorative SVG.The SVG has no accessible name. Since
CheckboxIndicatoris always placed inside an AriakitSelectItem/ComboboxItemthat already communicates selection state viaaria-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 SelectItemProps→type SelectItemPropsper coding guidelines.The
checkPositionprop is being added to this declaration in this PR, making it a touched symbol. Per the coding guidelines, TypeScriptinterfaceshould be replaced withtype.♻️ 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 everySelectItemto value changes — consider scoping to left-check items only.
store.useState(key)causes allSelectItemelements 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
useSyncExternalStorecalls in composite item components are Ariakit's identified performance bottleneck — each composite item may calluSESover 10 times, and React performs extra work with each such call.Adding
select?.useState("value")toSelectItemmeans every item (including those withcheckPosition === "right", the default) subscribes to the global selection value and re-renders on every toggle. With theTechnologyPickerrendering 60+ComboboxIteminstances, 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
📒 Files selected for processing (4)
apps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/webapp/app/components/primitives/CheckboxIndicator.tsxapps/webapp/app/components/primitives/Select.tsxapps/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/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/components/primitives/CheckboxIndicator.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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/coreusing subpaths only, never import from root
Files:
apps/webapp/app/components/primitives/CheckboxIndicator.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/webapp/app/components/primitives/Select.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/components/primitives/CheckboxIndicator.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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/corein 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 webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/components/primitives/CheckboxIndicator.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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.tsxapps/webapp/app/models/project.server.tsapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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.tsxapps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/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.tsxapps/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 inlinetypemodifier onOrganizationcorrectly avoids importing runtime values that are used only in type positions.
82-92: LGTM —onboardingDatais correctly threaded through retries and persisted.The recursive slug-collision retry path (lines 82–92) forwards
onboardingDataunchanged, and the Prismacreatecall (line 105) passes it directly. Prisma treatsundefinedas "omit the field", so whenonboardingDatais 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.
| {filteredOptions.length === 0 && !searchValue && ( | ||
| <div className="px-3 py-2 text-xs text-text-dimmed">No options</div> | ||
| )} |
There was a problem hiding this comment.
"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.
| {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.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)
161-169: Schema fields carry opaquez.string()for JSON-encoded arrays — consider documenting the contract.
workingOn,technologies,technologiesOther, andgoalsarez.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 callsJSON.parseon 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
📒 Files selected for processing (3)
apps/webapp/app/components/onboarding/TechnologyPicker.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/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/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty 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/coreusing 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
envexport ofenv.server.tsinstead of directly accessingprocess.envin 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/corein 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 webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
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_OTHERproperly centralizes the magic string (addressing a prior review). The Fisher-Yates shuffle is correctly implemented, andas consttuples provide good type narrowing for the option arrays.
69-108: LGTM — clean reusable multi-select wrapper.The
MultiSelectFieldcomponent cleanly encapsulates theSelectmulti-value pattern with a nice truncated display. Thetextcallback gracefully handles the empty-selection case.
298-310: LGTM — Page setup and form initialization.Standard Remix pattern with
useTypedLoaderData, conformuseForm, 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'sif (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.
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
Outdated
Show resolved
Hide resolved
| 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]); | ||
| }, []); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 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/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty 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/coreusing 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
envexport ofenv.server.tsinstead of directly accessingprocess.envin 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/corein 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 webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
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_OTHERas a single source of truth for the magic string addresses the prior review feedback.shuffleArrayis a correct Fisher-Yates shuffle, and theas consttuples are idiomatic.
69-108: LGTM —MultiSelectFieldis a clean, focused helper.Good use of an inline type for props and a function declaration. The
textrender 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.
safeParseStringArraycorrectly isolates JSON parsing and Zod validation failures so they never abort project creation. The helper eliminates the previous code duplication and unsafeas 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 theWORKING_ON_OTHERselection. TheTechnologyPickerintegration 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.
There was a problem hiding this comment.
🚩 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
🧹 Nitpick comments (3)
apps/webapp/app/routes/confirm-basic-details.tsx (3)
5-5:RadioGroupis imported directly from Radix, bypassing the local primitive wrapper.
RadioGroupItemcomes from~/components/primitives/RadioButton, which also exports aRadioGroupwrapper aroundRadioGroupPrimitive.Root. Importing one component from the primitive and the other directly from@radix-ui/react-radio-groupcreates 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.
selectedReferralSourcestarts asundefinedwhileselectedRolestarts as"". Both are treated the same way downstream (falsy check in the action, and thetextprop guard on theSelect), but the differing conventions add minor cognitive overhead. Usingundefinedfor both (to represent "nothing selected") would be more uniform.♻️ Proposed fix
- const [selectedRole, setSelectedRole] = useState<string>(""); + const [selectedRole, setSelectedRole] = useState<string | undefined>(undefined);The
textprop already handlesundefinedcorrectly (v ? ... : undefined), and the hidden input'svalueattribute 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
onboardingDataare concrete strings. The type declaration allowsundefinedas a value, but no code assignsundefinedto any property. Prisma 6.14.0'sInputJsonValuetype fully supportsRecord<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
📒 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/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty 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/coreusing 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
envexport ofenv.server.tsinstead of directly accessingprocess.envin 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/corein 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 webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
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 theRadioGroup(line 324) andaria-labelledby="role-label"on theSelect(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.
onboardingDatacolonboardingDatacolNew.onboarding.demo.mp4