Skip to content

273 detailed user profile creation#305

Merged
InfinityBowman merged 28 commits into
mainfrom
273-detailed-user-profile-creation
Jan 18, 2026
Merged

273 detailed user profile creation#305
InfinityBowman merged 28 commits into
mainfrom
273-detailed-user-profile-creation

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Jan 18, 2026

Summary by CodeRabbit

  • New Features

    • New in-app UI library (buttons, dialogs, tooltips, menus, tabs, selects, file upload/dropzones, avatars, toasts, progress, spinners, steps, pin/QR/password inputs, editable fields) and two new mock pages: Project Wizard and Project View V2.
  • UX

    • Standardized, more accessible modal/dialog/alert/tooltip/collapsible patterns, consistent close controls, multi-step wizards, improved in-place editors, checkbox/switch refinements, and richer progress/preview visuals.
  • Database

    • Expanded user profile fields: title, institution, department, country, bio, timezone, locale, preferences, lastActiveAt.
  • Chores

    • Tooling and dependency updates; TypeScript and build config adjustments.

✏️ Tip: You can customize this high-level summary in your review settings.

@InfinityBowman InfinityBowman linked an issue Jan 18, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 18, 2026

📝 Walkthrough

Walkthrough

Replaces the shared UI package with a local Ark‑UI–based SolidJS component library under packages/web/src/components/ui, migrates many web components to those new primitives, adds user/profile DB fields + migrations, updates google‑drive API signature, and adds large demo/mock pages and related tooling/tsconfig updates.

Changes

Cohort / File(s) Summary
ESLint config
\eslint.config.js``
Added overrides disabling no-restricted-imports and corates/corates-ui-imports for packages/web/src/components/ui/** and packages/web/src/lib/ui/**.
New local UI library
**packages/web/src/components/ui/**
\dialog.tsx`, `tooltip.tsx`, `select.tsx`, `checkbox.tsx`, `file-upload.tsx`, `toast.tsx`, `avatar.tsx`, `cn.ts`, `z-index.ts`, ...`
Adds many Ark‑UI wrappers and new public primitives (Dialog, AlertDialog, TooltipTrigger/Content/Positioner, CollapsibleTrigger/Content, TabsList/Trigger, Menu primitives, Select/SimpleSelect, CheckboxRoot/Control/Label, FileUploadDropzone/HiddenInput, Toast API, Avatar/UserAvatar, cn, Z_INDEX, etc.). Review new prop shapes (onOpenChange, onCheckedChange, onValueChange).
Removed legacy UI package
**packages/ui/**
...
Deletes @corates/ui package source, exports, tests, and package.json. Ensure no remaining imports reference @corates/ui.
Web components migrated
**packages/web/src/components/**
e.g. admin/*, project/*, checklist/*, sidebar/*, settings/*, auth/*, billing/*, pdf/*, mocks/*
Rewires many components to use local primitives (Dialog → DialogBackdrop/Positioner/Content/Header/Body/CloseTrigger; Tooltip → TooltipTrigger/Positioner/Content; Collapsible → CollapsibleTrigger/Content; Tabs → TabsList/TabsTrigger/TabsContent; Menu → MenuTrigger/Positioner/Content/Item; Checkbox → CheckboxRoot/Control/Label; Switch → SwitchRoot/Control/Thumb; Select → SimpleSelect). Mostly internal composition changes; public component signatures largely preserved.
Alert/Confirm flows refactor
packages/web/src/components/**
Replaces programmatic confirm hooks with in-component AlertDialog/AlertDialogAction flows (new signals controlling dialog visibility). Verify confirmation paths and saving/deleting flows.
File upload & PDF flows
packages/web/src/components/**, packages/web/src/components/ui/file-upload.tsx
Introduces FileUploadDropzone and FileUploadHiddenInput; switches file uploads to onFileAccept + dropzone UI. Review accept formats and handlers.
Mocks / Demos added
packages/web/src/components/mocks/*
Adds large demo pages and routes: ProjectWizardMock, ProjectViewV2 and registers two mock routes. Heavy new UI code to review.
Schema / Auth / Migrations
packages/workers/src/db/schema.ts, packages/workers/src/auth/config.ts, packages/workers/migrations/*
Adds optional user/account fields: title, institution, department, country, bio, timezone, locale, preferences, lastActiveAt (DB, auth config, migrations updated).
API/client changes
packages/web/src/api/google-drive.js, packages/web/src/stores/**
importFromGoogleDrive signature changed (removed orgId) and endpoint switched to /api/google-drive/import; callsites updated. Some other calls were made org‑scoped elsewhere.
Global config & styles
packages/web/tsconfig.json, packages/web/src/global.css, packages/web/src/styles/ark-ui.css, packages/web/package.json
Adds TS config with path aliases, replaces external Ark stylesheet with local ark-ui.css, updates Tailwind content sources to include local UI files, and bumps/adds dependencies (@ark-ui/solid, class-variance-authority, clsx, typescript, tooling bumps).
Tooling / workspace
pnpm-workspace.yaml, various package.json
Adds protobufjs to workspace build list; dependency version bumps across workers/web packages.
Widespread import rewrites
many packages/web/src/**
Replaces @corates/ui imports with local @/components/ui/* modules and updates toast/toaster import/usage to ToasterContainer/showToast.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User as User
    participant Component as WebComponent
    participant AlertDialog as AlertDialog (UI)
    participant API as Server/API
    participant Toast as ToastService

    User->>Component: Click "Delete" / "Finish"
    Component->>AlertDialog: set open = true (show confirmation)
    AlertDialog->>User: render modal
    User->>AlertDialog: confirm action
    AlertDialog->>Component: invoke confirm handler
    Component->>API: perform delete/save request
    API-->>Component: success
    Component->>AlertDialog: set open = false (close)
    Component->>Toast: showToast.success(...)
    Toast-->>User: display toast
Loading
sequenceDiagram
    autonumber
    participant User as User
    participant CP as CompleteProfile Component
    participant Steps as Steps UI
    participant Auth as AuthService
    participant Toast as ToastService

    User->>CP: open CompleteProfile
    CP->>Steps: render step 1
    User->>Steps: fill inputs -> Next
    Steps->>CP: handleStepChange (validation)
    CP->>Steps: render step 2
    User->>Steps: finish -> Submit
    Steps->>CP: aggregate data
    CP->>Auth: auth.updateProfile({title,institution,department,...})
    Auth-->>CP: success
    CP->>Toast: showToast.success(...)
    Toast-->>User: display success
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '273 detailed user profile creation' clearly references the ticket/issue number (273) and concisely describes the main feature: adding detailed user profile creation functionality.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
corates 0483d36 Jan 18 2026, 06:07 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Jan 18, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
corates ce43844 Commit Preview URL Jan 18 2026, 08:52 PM

Copy link
Copy Markdown

@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: 13

Caution

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

⚠️ Outside diff range comments (1)
packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx (1)

81-82: Fix the misleading comment about a delay

The code sets selectsReady immediately; the comment implies a delay that does not exist.

Proposed fix
-      // Small delay to ensure Select components are ready before rendering
       setSelectsReady(true);

As per coding guidelines, avoid stale comments that contradict the code.

🤖 Fix all issues with AI agents
In `@packages/web/src/components/auth/RoleSelector.jsx`:
- Around line 11-13: getTitleLabel currently treats any falsy value (including
empty string) as absent and returns null, preventing the "None" label from being
returned; change the emptiness check to only treat null or undefined as absent
(e.g., use titleValue == null or titleValue === null || titleValue ===
undefined) so that an explicit empty string will fall through to the
TITLE_OPTIONS lookup (TITLE_OPTIONS.find(t => t.value === titleValue)?.label ||
titleValue) and return the "None" label when appropriate.

In `@packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx`:
- Around line 163-164: The onOpenChange handler in the Dialog component (in
ScoringSummary.jsx) calls props.onClose() unconditionally, which will throw if
onClose is not provided; update the handler used on the Dialog (the onOpenChange
prop) to guard before invoking props.onClose — e.g., check if props.onClose
exists or use optional chaining (props.onClose?.()) so closing the dialog is
safe when onClose is undefined.

In `@packages/web/src/components/checklist/ScoreTag.jsx`:
- Around line 83-88: In ScoreTag.jsx update the anchor that opens external
guidance (the <a> rendered with href={infoUrl()} and aria-label using
getChecklistMetadata(props.checklistType).name) to include "noopener" in its rel
attribute (e.g., change rel='noreferrer' to include noopener) so the new-tab
link uses rel="noreferrer noopener" to prevent tabnabbing; keep the existing
target='_blank' and accessibility label unchanged.

In `@packages/web/src/components/dashboard/ProjectsSection.jsx`:
- Around line 183-191: The Show condition uses a redundant null check on
canCreateProject()—that function always returns a boolean—so replace the
compound condition with a check that also respects subscription loading; change
the visibility logic to use subscriptionLoading() together with
canCreateProject() (e.g., only show the ContactPrompt when subscriptionLoading()
is false and canCreateProject() is false) so update the Show condition
referencing canCreateProject() and subscriptionLoading() and keep the
ContactPrompt props unchanged.

In `@packages/web/src/components/mocks/ProjectWizardMock.jsx`:
- Around line 752-783: The input element has two onFocus handlers so the first
(which calls setShowSuggestions(true)) is overwritten by the second that applies
styles; combine them by creating a single onFocus callback that both calls
setShowSuggestions(true) and applies the visual styles (same logic currently in
the second onFocus), keeping the existing onBlur behavior and preserving
emailInput(), setShowSuggestions, and tokens usage; update the input's onFocus
to reference that combined handler (or inline combine) so suggestion visibility
and styling both run.

In
`@packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx`:
- Around line 119-123: The DialogCloseTrigger control rendering the FiX icon in
AssignReviewersModal (the DialogCloseTrigger element containing <FiX />) lacks
an accessible name; update the DialogCloseTrigger to include an accessible label
by adding an aria-label (e.g., aria-label="Close") or a title prop on the
trigger element (or on the FiX icon) so screen readers announce its purpose,
ensuring the icon-only control is accessible.

In
`@packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx`:
- Around line 94-103: The Aim tooltip trigger uses a non-focusable div inside
TooltipTrigger, preventing keyboard users from opening the tooltip; replace that
div with a focusable element (preferably a <button type="button">) keeping the
same className/styles and contents so it receives keyboard focus and triggers
the Tooltip on focus/keyboard activation (or alternatively add tabIndex="0" and
role="button" plus key handlers), updating the element inside TooltipTrigger in
ROB2Navbar.jsx (the container currently with class 'mr-1 flex shrink-0...') to
be focusable.

In `@packages/web/src/components/project/todo-tab/TodoStudyRow.jsx`:
- Around line 94-96: The PDF count currently renders with a unicode middle dot;
update the UI string in the TodoStudyRow component where Show is used (the block
invoking hasPdfs() and pdfCount()) to use an ASCII separator such as " - " or "
| " instead of "·". Locate the Show wrapper around hasPdfs() and replace the
unicode middle dot in the text node that includes pdfCount() with the chosen
ASCII punctuation so the displayed string becomes e.g. " - {pdfCount()} PDFs".

In `@packages/web/src/components/sidebar/Sidebar.jsx`:
- Around line 154-162: confirmDeleteChecklist currently awaits deleteChecklist
without handling errors which can cause unhandled rejections and skip cleanup;
wrap the await deleteChecklist(checklistId) in a try/catch/finally: in try call
deleteChecklist(checklistId), in catch log/report the error and show a
user-facing message (toast/alert) so failures surface, and in finally always
call setDeleteDialogOpen(false) and setPendingDeleteId(null) to ensure cleanup;
reference confirmDeleteChecklist, deleteChecklist, setDeleteDialogOpen, and
setPendingDeleteId when making the change.

In `@packages/web/src/components/ui/alert-dialog.tsx`:
- Around line 246-263: The AlertDialogCloseTrigger is an icon-only button
missing an accessible label; update AlertDialogCloseTriggerProps to accept an
optional ariaLabel?: string (or label?: string) and pass
aria-label={props.ariaLabel ?? 'Close'} into DialogPrimitive.CloseTrigger (the
component returned by AlertDialogCloseTrigger) so the FiX icon button is
announced by screen readers; ensure the prop type and the
DialogPrimitive.CloseTrigger usage are updated accordingly (keep existing class,
disabled handling, and default label 'Close').

In `@packages/web/src/components/ui/button.tsx`:
- Around line 56-63: The Button component currently doesn't set an explicit
type, causing it to default to "submit" inside forms; update the JSX in the
Button component (function named Button) to include a default type="button"
attribute on the <button> element — place type="button" before spreading
{...others} so that callers can still override it via props if desired (or
alternatively add a type prop with default "button" in the splitProps handling
and pass that into the <button>).

In `@packages/web/src/components/ui/checkbox.tsx`:
- Around line 100-108: The Label component uses tailwind peer-disabled classes
that require a sibling with class "peer" but CheckboxHiddenInput does not set
that, so the styles never apply; fix by either adding the "peer" class to the
CheckboxHiddenInput element (where CheckboxHiddenInput is defined/used) or
change the classes on CheckboxPrimitive.Label to use Ark/HTML data attribute
selectors like data-[disabled]:cursor-not-allowed and data-[disabled]:opacity-70
so the disabled styling works without a peer sibling; update
CheckboxPrimitive.Label (and related class composition in cn(...)) and/or add
the "peer" class to CheckboxHiddenInput accordingly.

In `@packages/web/src/global.css`:
- Around line 4-9: The top comment describing Tailwind content sources is stale
(mentions `@corates/ui`) and contradicts the actual `@source` entries; update the
comment above the `@source` './components/ui/**/*.{js,jsx,ts,tsx}' and `@source`
'./lib/ui/**/*.{js,jsx,ts,tsx}' lines to accurately describe that local UI
component directories are being included as Tailwind content sources (remove or
replace the `@corates/ui` reference and any misleading wording), keeping the
intent clear and concise so the comment matches the `@source` declarations.
♻️ Duplicate comments (4)
packages/web/src/components/project/reconcile-tab/ReconcileStudyRow.jsx (2)

93-96: Duplicate: header click needs keyboard accessibility

Same keyboard accessibility concern as in packages/web/src/components/project/todo-tab/TodoStudyRow.jsx.


118-120: Duplicate: unicode separator in PDF count text

Same unicode separator concern as in packages/web/src/components/project/todo-tab/TodoStudyRow.jsx. As per coding guidelines, avoid unicode symbols in UI strings.

packages/web/src/components/project/completed-tab/CompletedStudyRow.jsx (2)

79-82: Duplicate: header click needs keyboard accessibility

Same keyboard accessibility concern as in packages/web/src/components/project/todo-tab/TodoStudyRow.jsx.


104-106: Duplicate: unicode separator in PDF count text

Same unicode separator concern as in packages/web/src/components/project/todo-tab/TodoStudyRow.jsx. As per coding guidelines, avoid unicode symbols in UI strings.

🧹 Nitpick comments (42)
packages/web/src/components/mocks/ProjectViewComplete.jsx (4)

640-748: Use <For> component instead of .map() for list rendering.

The FiguresTab component uses .map() on lines 650, 675, and 698, which breaks SolidJS reactivity best practices. As per coding guidelines, use Solid's <For> component for rendering lists.

Suggested fix for lines 645-659
       {/* Summary Stats */}
       <div class='grid grid-cols-4 gap-4'>
-        {[
+        <For each={[
           { label: 'Total Studies', value: '24', change: '+3 this week' },
           { label: 'High Confidence', value: '8', pct: '33%' },
           { label: 'Moderate', value: '10', pct: '42%' },
           { label: 'Low/Critically Low', value: '6', pct: '25%' },
-        ].map(stat => (
+        ]}>
+          {stat => (
           <div class='rounded-xl border border-slate-200 bg-white p-5'>
             <p class='text-sm text-slate-500'>{stat.label}</p>
             <div class='mt-1 flex items-end justify-between'>
               <span class='text-2xl font-bold text-slate-900'>{stat.value}</span>
               <span class='text-xs text-slate-400'>{stat.change || stat.pct}</span>
             </div>
           </div>
-        ))}
+          )}
+        </For>
       </div>

Apply similar changes to the chart bars (line 675) and domain grid (line 698).


1-6: Update the header comment to reflect the rename from "Charts" to "Figures".

Line 5 still references "Charts" in the design direction comment, but the tab has been renamed to "Figures".

Suggested fix
 /**
  * ProjectView Mock - Complete Workflow
  *
  * Design Direction: Light-mode dashboard with clear workflow stages.
- * Shows the full journey: Team -> Studies -> Assignment -> Review -> Reconcile -> Complete -> Charts
+ * Shows the full journey: Team -> Studies -> Assignment -> Review -> Reconcile -> Complete -> Figures
  */

316-318: Remove unused showUpload signal.

The showUpload signal is created but never used in StudiesTab. This is dead code.

Suggested fix
 function StudiesTab() {
-  const [showUpload, setShowUpload] = createSignal(false);
-
   const unassignedStudies = createMemo(() => mockStudies.filter(s => s.assignedTo.length === 0));

782-785: Clarify intent: empty avatars section with production comment.

The avatars section is now empty with only comments. If this is intentional for mock purposes, consider adding a TODO(agent): comment to track the production implementation, or remove the empty <div> entirely if it serves no purpose.

Option: Add TODO or remove empty container
-              {/* Online team members - powered by Yjs awareness, only shows users currently viewing this project */}
-              <div class='flex -space-x-2'>
-                {/* In production, this would filter by awareness state from useProject() */}
-              </div>
+              {/* TODO(agent): Render online team members using Yjs awareness from useProject() */}
packages/web/src/components/ui/cn.ts (1)

1-15: Consider widening cn input types to match clsx.

The current signature rejects arrays and objects supported by clsx, which can force casts at call sites. Consider using clsx's ClassValue type for parity.

Proposed fix
-import { clsx } from 'clsx';
+import { clsx, type ClassValue } from 'clsx';
 import { twMerge } from 'tailwind-merge';
@@
-export function cn(...inputs: Array<string | undefined | null | false>): string {
+export function cn(...inputs: ClassValue[]): string {
   return twMerge(clsx(inputs));
 }

Please confirm your clsx version exports ClassValue, or adjust the import accordingly.

packages/web/src/styles/ark-ui.css (1)

41-61: Extend reduced-motion handling to collapsible animations

Currently reduced-motion only disables select animations. Collapsible open and close animations should also be disabled to respect user preferences.

Proposed fix
 `@media` (prefers-reduced-motion: reduce) {
+  [data-scope='collapsible'][data-part='content'] {
+    animation: none;
+  }
   [data-scope='select'][data-part='content'] {
     animation: none;
   }
 }

Also applies to: 126-130

packages/web/src/components/ui/avatar.tsx (1)

23-81: Avoid splitProps to comply with the no-prop-destructuring rule

The repo guidelines state not to destructure props in SolidJS components. You can avoid splitProps by spreading props first and overriding class afterward.

Proposed fix
-import { splitProps } from 'solid-js';
@@
-const Avatar: Component<AvatarProps> = props => {
-  const [local, others] = splitProps(props, ['class', 'children']);
-  return (
-    <AvatarPrimitive.Root
-      class={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', local.class)}
-      {...others}
-    >
-      {local.children}
-    </AvatarPrimitive.Root>
-  );
-};
+const Avatar: Component<AvatarProps> = props => (
+  <AvatarPrimitive.Root
+    {...props}
+    class={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', props.class)}
+  >
+    {props.children}
+  </AvatarPrimitive.Root>
+);
@@
-const AvatarImage: Component<AvatarImageProps> = props => {
-  const [local, others] = splitProps(props, ['class']);
-  return (
-    <AvatarPrimitive.Image
-      class={cn('aspect-square h-full w-full object-cover', local.class)}
-      {...others}
-    />
-  );
-};
+const AvatarImage: Component<AvatarImageProps> = props => (
+  <AvatarPrimitive.Image
+    {...props}
+    class={cn('aspect-square h-full w-full object-cover', props.class)}
+  />
+);
@@
-const AvatarFallback: Component<AvatarFallbackProps> = props => {
-  const [local, others] = splitProps(props, ['class', 'children']);
-  return (
-    <AvatarPrimitive.Fallback
-      class={cn(
-        'flex h-full w-full items-center justify-center rounded-full bg-gray-100 text-sm font-medium text-gray-600',
-        local.class,
-      )}
-      {...others}
-    >
-      {local.children}
-    </AvatarPrimitive.Fallback>
-  );
-};
+const AvatarFallback: Component<AvatarFallbackProps> = props => (
+  <AvatarPrimitive.Fallback
+    {...props}
+    class={cn(
+      'flex h-full w-full items-center justify-center rounded-full bg-gray-100 text-sm font-medium text-gray-600',
+      props.class,
+    )}
+  >
+    {props.children}
+  </AvatarPrimitive.Fallback>
+);

As per coding guidelines, avoid prop destructuring in SolidJS components.

packages/web/src/components/mocks/ProjectViewV2.jsx (4)

357-376: Use For instead of Array.map for list rendering

Array.map in JSX bypasses Solid's list rendering conventions and can hurt fine-grained updates. Replace these lists with For.

Proposed fix
-        {[
-          { label: 'Total Studies', value: stats().total, color: tokens.slate600 },
-          { label: 'Completed', value: stats().completed, color: tokens.success },
-          { label: 'In Review', value: stats().inReview, color: tokens.blue600 },
-          {
-            label: 'Needs Action',
-            value: stats().needsReconcile + stats().unassigned,
-            color: tokens.warning,
-          },
-        ].map(stat => (
-          <div class='text-center'>
-            <p class='text-2xl font-bold' style={{ color: stat.color }}>
-              {stat.value}
-            </p>
-            <p class='text-sm' style={{ color: tokens.slate500 }}>
-              {stat.label}
-            </p>
-          </div>
-        ))}
+        <For
+          each={[
+            { label: 'Total Studies', value: stats().total, color: tokens.slate600 },
+            { label: 'Completed', value: stats().completed, color: tokens.success },
+            { label: 'In Review', value: stats().inReview, color: tokens.blue600 },
+            {
+              label: 'Needs Action',
+              value: stats().needsReconcile + stats().unassigned,
+              color: tokens.warning,
+            },
+          ]}
+        >
+          {stat => (
+            <div class='text-center'>
+              <p class='text-2xl font-bold' style={{ color: stat.color }}>
+                {stat.value}
+              </p>
+              <p class='text-sm' style={{ color: tokens.slate500 }}>
+                {stat.label}
+              </p>
+            </div>
+          )}
+        </For>

As per coding guidelines, use For for list rendering in SolidJS components.

Also applies to: 446-485


611-627: Replace Array.map and ternary rendering with For and Show

Use For for the Add Studies button list, and Show for the conditional avatar rendering inside the assigned list.

Proposed fix
-        {[
-          { icon: FiUpload, label: 'Upload PDFs', desc: 'Drag & drop' },
-          { icon: FiLink, label: 'DOI/PMID', desc: 'Lookup metadata' },
-          { icon: FiFile, label: 'Reference File', desc: 'RIS, BibTeX' },
-          { icon: FiExternalLink, label: 'Google Drive', desc: 'Connect' },
-        ].map(source => (
-          <button
-            class='flex flex-col items-center gap-2 rounded-lg border-2 border-dashed p-4 transition-all'
-            style={{ 'border-color': tokens.slate200, color: tokens.slate500 }}
-          >
-            <source.icon class='h-5 w-5' />
-            <span class='text-sm font-medium'>{source.label}</span>
-            <span class='text-xs' style={{ color: tokens.slate400 }}>
-              {source.desc}
-            </span>
-          </button>
-        ))}
+        <For
+          each={[
+            { icon: FiUpload, label: 'Upload PDFs', desc: 'Drag & drop' },
+            { icon: FiLink, label: 'DOI/PMID', desc: 'Lookup metadata' },
+            { icon: FiFile, label: 'Reference File', desc: 'RIS, BibTeX' },
+            { icon: FiExternalLink, label: 'Google Drive', desc: 'Connect' },
+          ]}
+        >
+          {source => (
+            <button
+              class='flex flex-col items-center gap-2 rounded-lg border-2 border-dashed p-4 transition-all'
+              style={{ 'border-color': tokens.slate200, color: tokens.slate500 }}
+            >
+              <source.icon class='h-5 w-5' />
+              <span class='text-sm font-medium'>{source.label}</span>
+              <span class='text-xs' style={{ color: tokens.slate400 }}>
+                {source.desc}
+              </span>
+            </button>
+          )}
+        </For>
@@
-                        {memberId => {
-                          const member = mockMembers.find(m => m.id === memberId);
-                          return member ?
-                              <Avatar name={member.name} size='26px' fontSize='9px' />
-                            : null;
-                        }}
+                        {memberId => {
+                          const member = mockMembers.find(m => m.id === memberId);
+                          return (
+                            <Show when={member}>
+                              <Avatar name={member.name} size='26px' fontSize='9px' />
+                            </Show>
+                          );
+                        }}

As per coding guidelines, use For and Show for list and conditional rendering in SolidJS components.

Also applies to: 756-761


985-1004: Use For instead of Array.map in ChartsTab lists

Array.map in JSX bypasses Solid's list rendering conventions. Use For for each of these lists.

Proposed fix
-        {[
-          { label: 'Total Studies', value: '24', sub: '+3 this week' },
-          { label: 'High Confidence', value: '8', sub: '33%' },
-          { label: 'Moderate', value: '10', sub: '42%' },
-          { label: 'Low/Critically Low', value: '6', sub: '25%' },
-        ].map(stat => (
-          <Card>
-            <p class='text-sm' style={{ color: tokens.slate500 }}>
-              {stat.label}
-            </p>
-            <div class='mt-1 flex items-end justify-between'>
-              <span class='text-2xl font-bold' style={{ color: tokens.slate900 }}>
-                {stat.value}
-              </span>
-              <span class='text-xs' style={{ color: tokens.slate400 }}>
-                {stat.sub}
-              </span>
-            </div>
-          </Card>
-        ))}
+        <For
+          each={[
+            { label: 'Total Studies', value: '24', sub: '+3 this week' },
+            { label: 'High Confidence', value: '8', sub: '33%' },
+            { label: 'Moderate', value: '10', sub: '42%' },
+            { label: 'Low/Critically Low', value: '6', sub: '25%' },
+          ]}
+        >
+          {stat => (
+            <Card>
+              <p class='text-sm' style={{ color: tokens.slate500 }}>
+                {stat.label}
+              </p>
+              <div class='mt-1 flex items-end justify-between'>
+                <span class='text-2xl font-bold' style={{ color: tokens.slate900 }}>
+                  {stat.value}
+                </span>
+                <span class='text-xs' style={{ color: tokens.slate400 }}>
+                  {stat.sub}
+                </span>
+              </div>
+            </Card>
+          )}
+        </For>

As per coding guidelines, use For for list rendering in SolidJS components.

Also applies to: 1017-1042, 1053-1070, 1083-1122


1267-1270: Avoid runtime font @import inside the component

Inline @import in a component can lead to repeated injections and CSP problems. Prefer loading the font once in global styles or the app shell.

packages/web/src/components/mocks/ProjectWizardMock.jsx (2)

1237-1256: Prefer For component over Array.map() for list rendering.

Per coding guidelines, SolidJS components should use the For component for rendering lists instead of Array.map().

Suggested refactor
       <div class='grid grid-cols-4 gap-2'>
-        {[
-          { name: 'RIS', desc: 'Research Info Systems' },
-          { name: 'BibTeX', desc: 'LaTeX bibliography' },
-          { name: 'EndNote', desc: 'XML export' },
-          { name: 'CSV', desc: 'Spreadsheet' },
-        ].map(format => (
-          <div
-            class='rounded-lg border p-3 text-center'
-            style={{ 'border-color': tokens.slate200, background: 'white' }}
-          >
-            <p class='text-sm font-medium' style={{ color: tokens.slate700 }}>
-              {format.name}
-            </p>
-            <p class='mt-0.5 text-xs' style={{ color: tokens.slate400 }}>
-              {format.desc}
-            </p>
-          </div>
-        ))}
+        <For each={[
+          { name: 'RIS', desc: 'Research Info Systems' },
+          { name: 'BibTeX', desc: 'LaTeX bibliography' },
+          { name: 'EndNote', desc: 'XML export' },
+          { name: 'CSV', desc: 'Spreadsheet' },
+        ]}>
+          {format => (
+            <div
+              class='rounded-lg border p-3 text-center'
+              style={{ 'border-color': tokens.slate200, background: 'white' }}
+            >
+              <p class='text-sm font-medium' style={{ color: tokens.slate700 }}>
+                {format.name}
+              </p>
+              <p class='mt-0.5 text-xs' style={{ color: tokens.slate400 }}>
+                {format.desc}
+              </p>
+            </div>
+          )}
+        </For>
       </div>

1325-1348: Another instance of Array.map() that should use For.

Same issue as above - prefer For component for list rendering in SolidJS.

packages/web/src/components/ui/spinner.tsx (1)

101-106: ButtonSpinner is simpler than the external library version.

Note that packages/ui/src/components/Spinner.tsx has a more feature-rich ButtonSpinner with loading and children props for conditional rendering. This implementation is a simple preset spinner. Consider whether the conditional loading pattern would be useful here.

The current approach works for inline composition like:

<Button disabled={loading()}>
  <Show when={loading()}><ButtonSpinner /></Show>
  {loading() ? 'Saving...' : 'Save'}
</Button>

The external library approach wraps content:

<ButtonSpinner loading={loading()}>Save</ButtonSpinner>

Both patterns are valid - the simpler approach here is fine for a local UI library.

packages/web/src/components/ui/checkbox.tsx (1)

40-41: Consider removing the Checkbox alias to avoid confusion.

Both Checkbox (line 40) and CheckboxRoot (line 51) export the same underlying component with different APIs. CheckboxRoot wraps the primitive with custom onCheckedChange handling, while Checkbox is the raw primitive. This could lead to confusion about which to use.

If the raw primitive is needed for advanced use cases, consider renaming it to CheckboxPrimitive for clarity, or remove it if CheckboxRoot covers all use cases.

packages/web/src/components/ui/tooltip.tsx (1)

102-106: Consider using Show component for conditional rendering.

Per coding guidelines, SolidJS components should use the Show component for conditional rendering instead of && or ternary operators.

Suggested fix
       {...others}
     >
-      {showArrow() && (
+      <Show when={showArrow()}>
         <TooltipPrimitive.Arrow>
           <TooltipPrimitive.ArrowTip class='bg-gray-900' />
         </TooltipPrimitive.Arrow>
-      )}
+      </Show>
       {local.children}
     </TooltipPrimitive.Content>
packages/web/src/components/project/reconcile-tab/rob2-reconcile/NavbarDomainPill.jsx (1)

89-93: Prefer Show over ternary for the icon swap inside the conditional block.
This is a render-time condition, so using Show keeps conditional rendering consistent.

Proposed refactor
-            <Show when={props.sectionKey !== 'overall'}>
-              {props.isExpanded ?
-                <FiChevronDown class='h-3 w-3 opacity-60' />
-              : <FiChevronRight class='h-3 w-3 opacity-60' />}
-            </Show>
+            <Show when={props.sectionKey !== 'overall'}>
+              <Show
+                when={props.isExpanded}
+                fallback={<FiChevronRight class='h-3 w-3 opacity-60' />}
+              >
+                <FiChevronDown class='h-3 w-3 opacity-60' />
+              </Show>
+            </Show>

As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/project/reconcile-tab/amstar2-reconcile/ChecklistReconciliation.jsx (1)

418-420: Use Show for the saving label to match SolidJS conditional rendering guidance.

Proposed refactor
-                <AlertDialogAction variant='warning' disabled={saving()} onClick={confirmSave}>
-                  {saving() ? 'Saving...' : 'Finish'}
-                </AlertDialogAction>
+                <AlertDialogAction variant='warning' disabled={saving()} onClick={confirmSave}>
+                  <Show when={!saving()} fallback='Saving...'>
+                    Finish
+                  </Show>
+                </AlertDialogAction>

As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Reconciliation.jsx (1)

443-446: Use Show for the saving label to follow SolidJS conditional rendering guidance.

Proposed refactor
-                <AlertDialogAction variant='warning' disabled={saving()} onClick={confirmSave}>
-                  {saving() ? 'Saving...' : 'Finish'}
-                </AlertDialogAction>
+                <AlertDialogAction variant='warning' disabled={saving()} onClick={confirmSave}>
+                  <Show when={!saving()} fallback='Saving...'>
+                    Finish
+                  </Show>
+                </AlertDialogAction>

As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/ui/popover.tsx (1)

57-81: Avoid prop destructuring via splitProps in Solid components.
Project rules require direct props access to preserve reactivity; consider using props.field and composing class names without splitting.

As per coding guidelines, avoid destructuring SolidJS props.

Also applies to: 88-103, 109-123, 129-137, 143-151, 158-171, 177-180, 186-194

packages/web/src/components/ui/tabs.tsx (1)

47-129: Avoid prop destructuring via splitProps in Solid components.
Please align with the repo rule to access props directly and compose class names without splitting.

As per coding guidelines, avoid destructuring SolidJS props.

packages/web/src/components/checklist/ROBINSIChecklist/ScoringSummary.jsx (1)

194-248: Consider using DialogHeader, DialogBody, and DialogCloseTrigger for consistency.

The ResourcesDialog uses manual div wrappers with border styling instead of the new DialogHeader, DialogBody, and DialogCloseTrigger components. While functional, this creates inconsistency with other dialogs in this PR that use the full composition pattern (e.g., GoogleDrivePickerModal, EditPdfMetadataModal, PdfListItem).

Suggested refactor for consistency
-import {
-  Dialog,
-  DialogBackdrop,
-  DialogPositioner,
-  DialogContent,
-  DialogTitle,
-  DialogDescription,
-} from '@/components/ui/dialog';
+import {
+  Dialog,
+  DialogBackdrop,
+  DialogPositioner,
+  DialogContent,
+  DialogHeader,
+  DialogTitle,
+  DialogDescription,
+  DialogBody,
+  DialogCloseTrigger,
+} from '@/components/ui/dialog';
+import { FiX } from 'solid-icons/fi';

Then update the dialog structure:

         <DialogContent class='max-h-[85vh] max-w-md overflow-auto'>
-          <div class='border-b border-gray-200 px-6 py-4'>
+          <DialogHeader>
             <DialogTitle>ROBINS-I V2 Resources</DialogTitle>
-            <DialogDescription class='mt-1'>
-              Official guidance and documentation for the ROBINS-I assessment tool.
-            </DialogDescription>
-          </div>
+            <DialogCloseTrigger>
+              <FiX class='h-5 w-5' />
+            </DialogCloseTrigger>
+          </DialogHeader>
+          <DialogBody>
+            <DialogDescription>
+              Official guidance and documentation for the ROBINS-I assessment tool.
+            </DialogDescription>
 
-          <div class='space-y-4 px-6 py-4'>
+            <div class='mt-4 space-y-4'>
             {/* ... resource links ... */}
-          </div>
-
-          <div class='border-t border-gray-200 px-6 py-3'>
-            <button ...>Close</button>
-          </div>
+            </div>
+          </DialogBody>
         </DialogContent>
packages/web/src/components/project/all-studies-tab/EditPdfMetadataModal.jsx (1)

193-213: Consider using DialogFooter for action buttons.

The action buttons are placed inside DialogBody with a manual border-t separator. The DialogFooter component provides a consistent footer style with bg-gray-50 and standardized spacing. This would improve consistency across dialogs.

Optional: Use DialogFooter for actions

Add DialogFooter to imports:

 import {
   Dialog,
   DialogBackdrop,
   DialogPositioner,
   DialogContent,
   DialogHeader,
   DialogTitle,
   DialogBody,
   DialogCloseTrigger,
+  DialogFooter,
 } from '@/components/ui/dialog';

Then move action buttons:

-              {/* Actions */}
-              <div class='flex justify-end gap-3 border-t border-gray-200 pt-4'>
-                <button ...>Cancel</button>
-                <button ...>Save Changes</button>
-              </div>
             </div>
           </DialogBody>
+          <DialogFooter>
+            <button ...>Cancel</button>
+            <button ...>Save Changes</button>
+          </DialogFooter>
         </DialogContent>
packages/web/src/components/settings/pages/MergeAccountsDialog.jsx (1)

239-334: Use Show instead of ternary expressions in JSX

Several new conditional fragments use ternary operators (dialog title, help text, placeholder, loader icon). Please refactor these to <Show> to keep conditional rendering consistent across SolidJS components. As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/settings/pages/SessionManagement.jsx (1)

9-18: Prefer local toast module over @corates/ui

The PR introduces the local toast system; switching here avoids mixing toast APIs and keeps the UI migration consistent.

Proposed change
-import { showToast } from '@corates/ui';
+import { showToast } from '@/components/ui/toast';
packages/web/src/components/project/reconcile-tab/robins-i-reconcile/NavbarDomainPill.jsx (1)

90-94: Replace ternary icon toggle with Show

Use Show instead of a ternary for conditional rendering.

Proposed change
-            <Show when={props.sectionKey !== 'overall'}>
-              {props.isExpanded ?
-                <FiChevronDown class='h-3 w-3 opacity-60' />
-              : <FiChevronRight class='h-3 w-3 opacity-60' />}
-            </Show>
+            <Show when={props.sectionKey !== 'overall'}>
+              <Show
+                when={props.isExpanded}
+                fallback={<FiChevronRight class='h-3 w-3 opacity-60' />}
+              >
+                <FiChevronDown class='h-3 w-3 opacity-60' />
+              </Show>
+            </Show>

As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/ui/toast.tsx (1)

52-109: Avoid splitProps to comply with no-props-destructuring rule

splitProps is a form of props destructuring. Prefer direct props access and apply the same pattern across the toast wrapper components. If you do this, splitProps can be removed from the imports.

Proposed change
-const ToastRoot: Component<ToastRootProps> = props => {
-  const [local, others] = splitProps(props, ['class', 'children']);
-  return (
-    <ToastPrimitive.Root
-      class={cn(
-        'toast-item pointer-events-auto w-full max-w-sm rounded-lg border border-gray-200 bg-white p-4 shadow-lg',
-        local.class,
-      )}
-      {...others}
-    >
-      {local.children}
-    </ToastPrimitive.Root>
-  );
-};
+const ToastRoot: Component<ToastRootProps> = props => {
+  return (
+    <ToastPrimitive.Root
+      {...props}
+      class={cn(
+        'toast-item pointer-events-auto w-full max-w-sm rounded-lg border border-gray-200 bg-white p-4 shadow-lg',
+        props.class,
+      )}
+    >
+      {props.children}
+    </ToastPrimitive.Root>
+  );
+};

As per coding guidelines, avoid prop destructuring in Solid components.

packages/web/src/components/project/ProjectsPanel.jsx (1)

264-270: Use Show for delete button label

Replace the ternary with Show for conditional rendering.

Proposed change
-                {deleteLoading() ? 'Deleting...' : 'Delete Project'}
+                <Show when={deleteLoading()} fallback="Delete Project">
+                  Deleting...
+                </Show>

As per coding guidelines, use Show for conditional rendering.

packages/web/src/components/ui/steps.tsx (1)

78-84: Avoid splitProps in Steps wrappers

Props destructuring is disallowed; use props directly and merge class names. Apply the same pattern across the Steps wrappers.

Proposed change
-const StepsList: Component<StepsListProps> = props => {
-  const [local, others] = splitProps(props, ['class', 'children']);
-  return (
-    <StepsPrimitive.List class={cn('flex items-center gap-2', local.class)} {...others}>
-      {local.children}
-    </StepsPrimitive.List>
-  );
-};
+const StepsList: Component<StepsListProps> = props => {
+  return (
+    <StepsPrimitive.List {...props} class={cn('flex items-center gap-2', props.class)}>
+      {props.children}
+    </StepsPrimitive.List>
+  );
+};

As per coding guidelines, avoid prop destructuring in Solid components.

packages/web/src/components/ui/select.tsx (1)

64-74: Avoid splitProps in Select wrappers

Use props directly and override handlers after spreading. Apply the same pattern across the Select wrappers.

Proposed change
-const Select = <T,>(props: SelectProps<T>) => {
-  const [local, others] = splitProps(props, ['children', 'onValueChange', 'onOpenChange']);
-  return (
-    <SelectPrimitive.Root
-      onValueChange={details => local.onValueChange?.(details.value)}
-      onOpenChange={details => local.onOpenChange?.(details.open)}
-      {...others}
-    >
-      {local.children}
-    </SelectPrimitive.Root>
-  );
-};
+const Select = <T,>(props: SelectProps<T>) => {
+  return (
+    <SelectPrimitive.Root
+      {...props}
+      onValueChange={details => props.onValueChange?.(details.value)}
+      onOpenChange={details => props.onOpenChange?.(details.open)}
+    >
+      {props.children}
+    </SelectPrimitive.Root>
+  );
+};

As per coding guidelines, avoid prop destructuring in Solid components.

packages/web/src/components/dashboard/LocalAppraisalsSection.jsx (1)

96-116: Consider adding loading state during deletion.

The confirmDelete function is async but doesn't indicate loading state to the user. Users may click "Delete" multiple times if the operation takes time.

Optional: Add loading state to prevent double-clicks
+ const [isDeleting, setIsDeleting] = createSignal(false);

  const confirmDelete = async () => {
    const checklistId = pendingDeleteId();
-   if (checklistId) {
+   if (checklistId && !isDeleting()) {
+     setIsDeleting(true);
      await deleteChecklist(checklistId);
+     setIsDeleting(false);
    }
    setDeleteDialogOpen(false);
    setPendingDeleteId(null);
  };

Then in the AlertDialogAction:

- <AlertDialogAction variant='danger' onClick={confirmDelete}>
+ <AlertDialogAction variant='danger' onClick={confirmDelete} disabled={isDeleting()}>
-   Delete
+   {isDeleting() ? 'Deleting...' : 'Delete'}
  </AlertDialogAction>
packages/web/src/components/checklist/LocalAppraisalsPanel.jsx (1)

38-59: Code duplication with LocalAppraisalsSection.jsx.

This delete confirmation pattern (signals, handleDelete, confirmDelete, AlertDialog) is nearly identical to LocalAppraisalsSection.jsx. Consider extracting a reusable hook or component to reduce duplication.

Extract shared delete confirmation logic

Create a primitive hook in packages/web/src/primitives/:

// useDeleteConfirmation.js
import { createSignal } from 'solid-js';

export function useDeleteConfirmation(onDelete) {
  const [dialogOpen, setDialogOpen] = createSignal(false);
  const [pendingId, setPendingId] = createSignal(null);
  const [isDeleting, setIsDeleting] = createSignal(false);

  const requestDelete = (id) => {
    setPendingId(id);
    setDialogOpen(true);
  };

  const confirmDelete = async () => {
    const id = pendingId();
    if (id && !isDeleting()) {
      setIsDeleting(true);
      await onDelete(id);
      setIsDeleting(false);
    }
    setDialogOpen(false);
    setPendingId(null);
  };

  const cancelDelete = () => {
    setDialogOpen(false);
    setPendingId(null);
  };

  return {
    dialogOpen,
    setDialogOpen,
    isDeleting,
    requestDelete,
    confirmDelete,
    cancelDelete,
  };
}

Then use it in both components:

const { dialogOpen, setDialogOpen, requestDelete, confirmDelete } = 
  useDeleteConfirmation(deleteChecklist);

Based on learnings, reusable logic should be extracted to primitives in packages/web/src/primitives/.

packages/web/src/components/settings/SettingsSidebar.jsx (1)

106-114: Minor: Consider using a signal for pathname tracking.

The lastPathname variable is mutated within an effect closure. While this works, using a signal would be more idiomatic in SolidJS and clearer about the reactive intent.

Optional: Use signal for clearer reactivity
- // Close mobile sidebar when route changes
- let lastPathname = location.pathname;
- createEffect(() => {
-   const currentPathname = location.pathname;
-   if (props.mobileOpen && currentPathname !== lastPathname) {
-     props.onCloseMobile?.();
-   }
-   lastPathname = currentPathname;
- });
+ // Close mobile sidebar when route changes
+ const [lastPathname, setLastPathname] = createSignal(location.pathname);
+ createEffect(() => {
+   const currentPathname = location.pathname;
+   if (props.mobileOpen && currentPathname !== lastPathname()) {
+     props.onCloseMobile?.();
+   }
+   setLastPathname(currentPathname);
+ });
packages/web/src/components/project/overview-tab/OverviewTab.jsx (2)

423-450: Use Show for conditional trigger text

The helper text in the triggers uses ternary rendering. Prefer Show with fallback and apply the same pattern to both sections. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-                {chartsExpanded() ? 'Click to collapse' : 'Click to expand charts'}
+                <Show when={chartsExpanded()} fallback='Click to expand charts'>
+                  Click to collapse
+                </Show>

468-490: Prefer Show for conditional dialog text and clear pending state on close

The dialog title, description, and action label use ternary expressions. Use Show with fallbacks, and clear pendingRemoveMember when the dialog closes to avoid stale values. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-      <AlertDialog open={removeDialogOpen()} onOpenChange={setRemoveDialogOpen}>
+      <AlertDialog
+        open={removeDialogOpen()}
+        onOpenChange={open => {
+          setRemoveDialogOpen(open);
+          if (!open) setPendingRemoveMember(null);
+        }}
+      >
...
-                {pendingRemoveMember()?.isSelf ? 'Leave Project' : 'Remove Member'}
+                <Show when={pendingRemoveMember()?.isSelf} fallback='Remove Member'>
+                  Leave Project
+                </Show>
packages/web/src/components/admin/OrgDetail.jsx (1)

459-501: Replace ternary dialog title and message with Show

The confirm dialog uses ternary expressions for both title and message. Prefer Show with fallback and apply the same pattern to the message paragraph. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-                  {confirmDialog()?.type === 'cancel-subscription' ?
-                    'Cancel Subscription'
-                  : 'Revoke Grant'}
+                  <Show when={confirmDialog()?.type === 'cancel-subscription'} fallback='Revoke Grant'>
+                    Cancel Subscription
+                  </Show>
packages/web/src/components/admin/ProjectDetail.jsx (1)

520-573: Replace ternary dialog title with Show

The confirm dialog title uses nested ternary expressions. Prefer Show with fallback for conditional rendering. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-                  {confirmDialog()?.type === 'delete-project' ?
-                    'Delete Project'
-                  : confirmDialog()?.type === 'remove-member' ?
-                    'Remove Member'
-                  : 'Confirm'}
+                  <Show
+                    when={confirmDialog()?.type === 'delete-project'}
+                    fallback={
+                      <Show when={confirmDialog()?.type === 'remove-member'} fallback='Confirm'>
+                        Remove Member
+                      </Show>
+                    }
+                  >
+                    Delete Project
+                  </Show>
packages/web/src/components/admin/StorageManagement.jsx (1)

419-449: Prefer Show for pluralization

The plural suffix uses ternary rendering. Use Show to conditionally render the suffix. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-                  Are you sure you want to delete {deleteDialog()?.length || 0} document
-                  {deleteDialog()?.length === 1 ? '' : 's'}? This action cannot be undone.
+                  Are you sure you want to delete {deleteDialog()?.length || 0} document
+                  <Show when={(deleteDialog()?.length || 0) !== 1}>s</Show>? This action cannot be undone.
packages/web/src/components/admin/UserDetail.jsx (1)

691-734: Replace ternary dialog title with Show

The confirm dialog title uses a ternary expression. Prefer Show with fallback for conditional rendering. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
-                  {confirmDialog()?.type === 'delete' ? 'Delete User' : 'Revoke All Sessions'}
+                  <Show when={confirmDialog()?.type === 'delete'} fallback='Revoke All Sessions'>
+                    Delete User
+                  </Show>
packages/web/src/components/admin/SubscriptionDialog.jsx (1)

6-16: Use Show for title and submit label states

The title and submit label rely on nested ternary rendering. Replace with Show blocks and add a Show import to align with Solid conditional rendering guidance. As per coding guidelines, use Show for conditional rendering in SolidJS.

Proposed change
+import { Show } from 'solid-js';
 import {
   Dialog,
   DialogBackdrop,
   DialogPositioner,
   DialogContent,
   DialogHeader,
   DialogTitle,
   DialogBody,
   DialogCloseTrigger,
 } from '@/components/ui/dialog';
 import { FiX } from 'solid-icons/fi';
...
-            <DialogTitle>{isEdit() ? 'Edit Subscription' : 'Create Subscription'}</DialogTitle>
+            <DialogTitle>
+              <Show when={isEdit()} fallback='Create Subscription'>
+                Edit Subscription
+              </Show>
+            </DialogTitle>
...
-                  {loading() ?
-                    isEdit() ?
-                      'Updating...'
-                    : 'Creating...'
-                  : isEdit() ?
-                    'Update'
-                  : 'Create'}
+                  <Show
+                    when={loading()}
+                    fallback={
+                      <Show when={isEdit()} fallback='Create'>
+                        Update
+                      </Show>
+                    }
+                  >
+                    <Show when={isEdit()} fallback='Creating...'>
+                      Updating...
+                    </Show>
+                  </Show>

Also applies to: 75-219

packages/web/src/components/profile/ProfilePage.jsx (1)

201-204: Unnecessary optional chaining on signal returns.

Signals always return a value (string in this case), so the ?. on editInstitution() and editDepartment() is redundant. This is a minor style observation and doesn't affect functionality.

Suggested simplification
       await auth.updateProfile({
         title: editTitle() || null,
-        institution: editInstitution()?.trim() || null,
-        department: editDepartment()?.trim() || null,
+        institution: editInstitution().trim() || null,
+        department: editDepartment().trim() || null,
       });
packages/workers/src/db/schema.ts (1)

19-29: Prefer intent-focused comments over descriptive field notes.
These inline comments mostly restate field meaning; consider removing or rephrasing to document intent, constraints, or validation expectations.

Proposed change
-  // Academic/professional information
-  title: text('title'), // 'Dr.', 'Prof.', or custom
-  institution: text('institution'), // University or organization name
-  department: text('department'), // Department or faculty name
-  country: text('country'), // ISO 3166-1 alpha-2 code (e.g., 'US', 'GB')
-  bio: text('bio'), // Short biography
-  // User preferences and activity
-  timezone: text('timezone'), // IANA timezone (e.g., 'America/New_York')
-  locale: text('locale'), // BCP 47 language tag (e.g., 'en-US')
-  preferences: text('preferences'), // JSON: { theme, emailNotifications, etc. }
+  title: text('title'),
+  institution: text('institution'),
+  department: text('department'),
+  country: text('country'),
+  bio: text('bio'),
+  timezone: text('timezone'),
+  locale: text('locale'),
+  preferences: text('preferences'),

As per coding guidelines, keep comments focused on intent and constraints.

Comment thread packages/web/src/components/auth/RoleSelector.jsx
Comment thread packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx Outdated
Comment thread packages/web/src/components/checklist/ScoreTag.jsx
Comment thread packages/web/src/components/dashboard/ProjectsSection.jsx Outdated
Comment on lines +752 to +783
<input
type='email'
value={emailInput()}
onInput={e => {
setEmailInput(e.target.value);
setShowSuggestions(true);
}}
onFocus={() => setShowSuggestions(true)}
onKeyDown={e => {
if (e.key === 'Enter' && emailInput()) {
e.preventDefault();
addMember(emailInput());
}
}}
placeholder='Email address or name'
class='w-full rounded-lg border py-2.5 pr-3 pl-10 text-sm transition-all duration-200 outline-none'
style={{
'border-color': tokens.slate200,
background: 'white',
color: tokens.slate900,
}}
onFocus={e => {
e.target.style.borderColor = tokens.blue500;
e.target.style.boxShadow = `0 0 0 3px ${tokens.blue100}`;
}}
onBlur={e => {
e.target.style.borderColor = tokens.slate200;
e.target.style.boxShadow = 'none';
// Delay hiding suggestions to allow click
setTimeout(() => setShowSuggestions(false), 150);
}}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Duplicate onFocus property on input element.

Static analysis correctly identifies that onFocus is assigned twice on this input element (lines 759 and 773). The second handler will override the first, so the autocomplete suggestion visibility logic on line 759 will be lost.

Proposed fix: Combine the onFocus handlers
             <input
               type='email'
               value={emailInput()}
               onInput={e => {
                 setEmailInput(e.target.value);
                 setShowSuggestions(true);
               }}
-              onFocus={() => setShowSuggestions(true)}
               onKeyDown={e => {
                 if (e.key === 'Enter' && emailInput()) {
                   e.preventDefault();
                   addMember(emailInput());
                 }
               }}
               placeholder='Email address or name'
               class='w-full rounded-lg border py-2.5 pr-3 pl-10 text-sm transition-all duration-200 outline-none'
               style={{
                 'border-color': tokens.slate200,
                 background: 'white',
                 color: tokens.slate900,
               }}
               onFocus={e => {
+                setShowSuggestions(true);
                 e.target.style.borderColor = tokens.blue500;
                 e.target.style.boxShadow = `0 0 0 3px ${tokens.blue100}`;
               }}
🧰 Tools
🪛 Biome (2.1.2)

[error] 759-759: This JSX property is assigned multiple times.

This attribute is assigned again here.

(lint/suspicious/noDuplicateJsxProps)

🤖 Prompt for AI Agents
In `@packages/web/src/components/mocks/ProjectWizardMock.jsx` around lines 752 -
783, The input element has two onFocus handlers so the first (which calls
setShowSuggestions(true)) is overwritten by the second that applies styles;
combine them by creating a single onFocus callback that both calls
setShowSuggestions(true) and applies the visual styles (same logic currently in
the second onFocus), keeping the existing onBlur behavior and preserving
emailInput(), setShowSuggestions, and tokens usage; update the input's onFocus
to reference that combined handler (or inline combine) so suggestion visibility
and styling both run.

Comment on lines +94 to +96
<Show when={hasPdfs()}>
<span class='text-gray-400'> · {pdfCount()} PDFs</span>
</Show>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid unicode symbols in the PDF count text

The separator between the citation and PDF count uses a unicode middle dot. Please replace it with ASCII punctuation (for example, " - " or " | ") to comply with the no-unicode rule. As per coding guidelines, avoid unicode symbols in UI strings.

🤖 Prompt for AI Agents
In `@packages/web/src/components/project/todo-tab/TodoStudyRow.jsx` around lines
94 - 96, The PDF count currently renders with a unicode middle dot; update the
UI string in the TodoStudyRow component where Show is used (the block invoking
hasPdfs() and pdfCount()) to use an ASCII separator such as " - " or " | "
instead of "·". Locate the Show wrapper around hasPdfs() and replace the unicode
middle dot in the text node that includes pdfCount() with the chosen ASCII
punctuation so the displayed string becomes e.g. " - {pdfCount()} PDFs".

Comment thread packages/web/src/components/sidebar/Sidebar.jsx
Comment thread packages/web/src/components/ui/alert-dialog.tsx
Comment thread packages/web/src/components/ui/button.tsx
Comment thread packages/web/src/global.css Outdated
Copy link
Copy Markdown

@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

🤖 Fix all issues with AI agents
In `@packages/web/src/components/ui/checkbox.tsx`:
- Around line 100-108: The Tailwind data attribute syntax used on
CheckboxPrimitive.Label is incorrect—replace the invalid `data-disabled:` usages
with the proper arbitrary attribute form `data-[disabled]:` so the disabled
styles apply; update the class construction in the CheckboxPrimitive.Label
render (where cn is called with 'text-sm leading-none font-medium
text-gray-900', 'data-disabled:cursor-not-allowed data-disabled:opacity-70',
local.class, and {...others}) to use 'data-[disabled]:cursor-not-allowed' and
'data-[disabled]:opacity-70' instead.
- Around line 71-81: The disabled styles on CheckboxPrimitive.Control use the
native CSS pseudo-class ("disabled:cursor-not-allowed disabled:opacity-50")
which won't apply because Control renders a wrapper element; change those to Ark
UI's data-attribute pattern to mirror the existing state styling (e.g. replace
"disabled:cursor-not-allowed disabled:opacity-50" with
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50" in the class
list for CheckboxPrimitive.Control so the wrapper responds to the component's
disabled state).
♻️ Duplicate comments (3)
packages/web/src/components/auth/RoleSelector.jsx (1)

11-14: Previous issue resolved.

The null check now correctly uses titleValue == null, allowing an explicit empty string to fall through to the lookup and return the "None" label as intended.

packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx (1)

114-124: LGTM - Dialog structure and accessibility.

The composed dialog structure (DialogDialogBackdropDialogPositionerDialogContentDialogHeader/DialogBody) follows the Ark UI composition pattern correctly. The aria-label='Close' on the DialogCloseTrigger (line 121) addresses the accessibility concern from the previous review.

packages/web/src/components/ui/button.tsx (1)

56-66: LGTM! Previous concern addressed.

The type='button' default (line 61) prevents accidental form submissions while still allowing callers to override via props. The splitProps pattern correctly maintains SolidJS reactivity without destructuring props.

🧹 Nitpick comments (1)
packages/web/src/components/dashboard/ProjectsSection.jsx (1)

79-167: Consider clearing pendingDeleteId when the dialog closes.

This avoids stale state if the dialog is dismissed via backdrop or escape.

Proposed fix
-      <AlertDialog open={deleteDialogOpen()} onOpenChange={setDeleteDialogOpen}>
+      <AlertDialog
+        open={deleteDialogOpen()}
+        onOpenChange={open => {
+          setDeleteDialogOpen(open);
+          if (!open) setPendingDeleteId(null);
+        }}
+      >

Also applies to: 248-274

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0483d36 and 6868551.

📒 Files selected for processing (12)
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/mocks/ProjectViewV2.jsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/global.css
🧰 Additional context used
📓 Path-based instructions (17)
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/src/**

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

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/global.css
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
**/*.{js,ts,jsx,tsx}

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

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/global.css
  • packages/web/src/components/ui/alert-dialog.tsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/auth/RoleSelector.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/src/**/*.{ts,tsx}

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

packages/web/src/**/*.{ts,tsx}: For UI icons, use solid-icons library or SVGs only (never emojis)
Use import aliases from jsconfig.json as defined for the project
Use Ark UI component library (@ark-ui/solid) for UI components
Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)
Do not prop-drill application state in SolidJS components; import stores directly where needed
Do not destructure props in SolidJS; access props.field directly or wrap in a function: () => props.field
Components should receive at most 1-5 props (local config only, not shared state)
Use createMemo for derived values in SolidJS
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state; import stores directly

Use import aliases from jsconfig.json as defined in the project configuration

packages/web/src/**/*.{ts,tsx}: Import stores directly and never prop-drill application state in SolidJS components
Never destructure SolidJS props - use props.field or () => props.field to preserve reactivity

Files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/web/src/components/**/*.{ts,tsx}

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

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/ui/alert-dialog.tsx
packages/{web,landing}/src/**/*.{ts,tsx}

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

packages/{web,landing}/src/**/*.{ts,tsx}: For UI icons, use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) or SVGs only, never emojis
Ensure browser compatibility, with particular attention to Safari
Group related components in subdirectories with barrel exports
Use Ark UI components from @ark-ui/solid for UI components
Use solid-icons icon library for all icon needs
Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure SolidJS props - access props.field directly or wrap in function: () => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Use createStore for complex state objects in SolidJS
Use createMemo for derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state - import stores directly

Files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/ui/alert-dialog.tsx
🧠 Learnings (55)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use Ark UI component library (`ark-ui/solid`) for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Use required libraries for specific functionality: Zod for validation, Drizzle for database, Better-Auth for authentication, Ark UI for components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Use getChecklistStatus utility from `@/lib/checklist-domain.js` to determine current checklist status before performing operations

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Checklist status transitions follow sequence: in_progress → completed (when all questions answered) → reconciled (after reconciliation complete)

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/AMSTAR2/checklist-compare.js} : Use compareChecklists utility from checklist-compare.js for comparing reviewer checklists

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js,packages/web/src/AMSTAR2/checklist-compare.js} : Store final answers in the reconciled checklist itself, not in reconciliation progress metadata

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/checkbox.tsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/ROBINS-I/** : Use scoreChecklist utility from `@/ROBINS-I/checklist.js` to determine risk of bias rating: 'Low' | 'Moderate' | 'Serious' | 'Critical'

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/ROBINS-I/** : Use shouldStopAssessment utility from `@/ROBINS-I/checklist.js` to check if Section B indicates critical risk of bias requiring assessment termination

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/lib/checklist-domain.js,packages/web/src/AMSTAR2/checklist-compare.js} : Reconciliation structure should contain checklist1Id, checklist2Id, and reconciledChecklistId

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `solid-icons` icon library for all icon needs

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createStore` for complex state objects in SolidJS

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : For UI icons, use `solid-icons` library (e.g., `solid-icons/bi`, `solid-icons/fi`) or SVGs only, never emojis

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's Show component for conditional rendering instead of ternary operators

Applied to files:

  • packages/web/src/components/sidebar/Sidebar.jsx
  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/** : Use scoreChecklist utility from `@/AMSTAR2/checklist.js` to determine quality assessment rating: 'High' | 'Moderate' | 'Low' | 'Critically Low'

Applied to files:

  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/** : Use NoteEditor component from `@/components/checklist-ui/common/NoteEditor.jsx` for question note editing with max 2000 character limit

Applied to files:

  • packages/web/src/components/checklist/ScoreTag.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/lib/checklist-domain.js : Register and retrieve checklists using checklist-registry: getChecklistType and getChecklistComponent functions

Applied to files:

  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Show and For components for conditional and list rendering in SolidJS

Applied to files:

  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/checklist/ScoreTag.jsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components

Applied to files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/global.css
  • packages/web/src/components/ui/alert-dialog.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use Ark UI component library (`ark-ui/solid`) for UI components

Applied to files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/checkbox.tsx
  • packages/web/src/global.css
  • packages/web/src/components/ui/alert-dialog.tsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Do NOT destructure SolidJS props - access `props.field` directly or wrap in function: `() => props.field`

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Do not prop-drill application state in SolidJS components; import stores directly where needed

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Do not destructure props in SolidJS; access `props.field` directly or wrap in a function: `() => props.field`

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `solid-icons` library for icons (e.g., `solid-icons/bi`, `solid-icons/fi`)

Applied to files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
📚 Learning: 2026-01-17T16:10:07.519Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Never destructure SolidJS props - use `props.field` or `() => props.field` to preserve reactivity

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : SolidJS components should receive at most 1-5 props (local config only, not shared state)

Applied to files:

  • packages/web/src/components/ui/button.tsx
  • packages/web/src/components/ui/alert-dialog.tsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Never destructure SolidJS props as it breaks reactivity

Applied to files:

  • packages/web/src/components/ui/button.tsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple reactive values in SolidJS components

Applied to files:

  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
  • packages/web/src/components/ui/alert-dialog.tsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple, reactive values in SolidJS

Applied to files:

  • packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx
  • packages/web/src/components/dashboard/ProjectsSection.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/components/project-ui/**/*.{js,jsx} : Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2026-01-06T23:56:57.354Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2026-01-06T23:56:57.354Z
Learning: Applies to packages/web/**/*.{jsx,tsx} : Use `createFormErrorSignals` for form error handling in frontend forms to manage field-level and global error states

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createStore for complex objects and arrays that need granular reactivity in SolidJS

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : When access is denied (user removed or project deleted), the connection automatically triggers cleanup: IndexedDB data cleared, connection closed, store cleared, and user redirected

Applied to files:

  • packages/web/src/components/dashboard/ProjectsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for computed/derived values that depend on reactive state in SolidJS

Applied to files:

  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for derived/computed values in SolidJS to maintain reactivity

Applied to files:

  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's For component for rendering lists instead of Array.map()

Applied to files:

  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/project/reconcile-tab/rob2-reconcile/ROB2Navbar.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Applied to files:

  • packages/web/src/components/ui/checkbox.tsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/** : AMSTAR2 answers must use matrix format: array of column arrays with boolean values representing checkbox selections per column

Applied to files:

  • packages/web/src/components/ui/checkbox.tsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Maintain separate answer format implementations for each checklist type (AMSTAR2, ROBINS-I, Generic) to prevent data corruption

Applied to files:

  • packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Comments should explain WHY something is being done or provide context, not repeat what the code is saying

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Use the Agent TODO convention `// TODO(agent): Brief description` for incomplete work, flagging items for future attention, known limitations, and documentation section references

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T16:10:07.519Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Comments should explain why something is being done, not what the code is doing

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Applied to files:

  • packages/web/src/global.css
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Ensure browser compatibility, with particular attention to Safari

Applied to files:

  • packages/web/src/global.css
🧬 Code graph analysis (5)
packages/web/src/components/ui/button.tsx (1)
packages/web/src/components/ui/cn.ts (1)
  • cn (13-15)
packages/web/src/components/dashboard/ProjectsSection.jsx (4)
packages/web/src/components/dashboard/LocalAppraisalsSection.jsx (2)
  • deleteDialogOpen (97-97)
  • pendingDeleteId (98-98)
packages/web/src/components/dashboard/Dashboard.jsx (1)
  • canCreateProject (45-51)
packages/web/src/components/project/ContactPrompt.jsx (1)
  • ContactPrompt (8-52)
packages/web/src/components/ui/alert-dialog.tsx (11)
  • AlertDialog (270-270)
  • AlertDialogBackdrop (271-271)
  • AlertDialogPositioner (272-272)
  • AlertDialogContent (273-273)
  • AlertDialogHeader (274-274)
  • AlertDialogIcon (275-275)
  • AlertDialogTitle (276-276)
  • AlertDialogDescription (277-277)
  • AlertDialogFooter (278-278)
  • AlertDialogCancel (279-279)
  • AlertDialogAction (280-280)
packages/web/src/components/ui/checkbox.tsx (2)
packages/ui/src/components/index.ts (1)
  • CheckboxPrimitive (7-7)
packages/web/src/components/ui/cn.ts (1)
  • cn (13-15)
packages/web/src/components/checklist/ROB2Checklist/ScoringSummary.jsx (3)
packages/web/src/components/ui/dialog.tsx (6)
  • Dialog (198-198)
  • DialogBackdrop (200-200)
  • DialogPositioner (201-201)
  • DialogContent (202-202)
  • DialogTitle (203-203)
  • DialogDescription (204-204)
packages/web/src/components/admin/GrantDialog.jsx (1)
  • open (35-35)
packages/web/src/components/admin/SubscriptionDialog.jsx (1)
  • open (48-48)
packages/web/src/components/ui/alert-dialog.tsx (3)
packages/ui/src/components/index.ts (1)
  • DialogPrimitive (11-11)
packages/web/src/components/ui/cn.ts (1)
  • cn (13-15)
packages/web/src/components/ui/z-index.ts (1)
  • Z_INDEX (15-47)
⏰ 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). (1)
  • GitHub Check: Workers Builds: corates

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +71 to +81
<CheckboxPrimitive.Control
class={cn(
'flex h-4 w-4 shrink-0 items-center justify-center rounded border border-gray-300',
'ring-offset-white transition-colors',
'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:outline-none',
'disabled:cursor-not-allowed disabled:opacity-50',
'data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white',
'data-[state=indeterminate]:border-blue-600 data-[state=indeterminate]:bg-blue-600 data-[state=indeterminate]:text-white',
local.class,
)}
{...others}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

disabled: pseudo-class won't work on Ark UI's Control wrapper.

The :disabled CSS pseudo-class only applies to native form elements. Since CheckboxPrimitive.Control renders a wrapper element (not an input), the disabled: utility classes won't take effect. Use Ark UI's data attribute pattern for consistency with the state styling on lines 77-78.

Suggested fix
       class={cn(
         'flex h-4 w-4 shrink-0 items-center justify-center rounded border border-gray-300',
         'ring-offset-white transition-colors',
         'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:outline-none',
-        'disabled:cursor-not-allowed disabled:opacity-50',
+        'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
         'data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white',
         'data-[state=indeterminate]:border-blue-600 data-[state=indeterminate]:bg-blue-600 data-[state=indeterminate]:text-white',
         local.class,
       )}
📝 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
<CheckboxPrimitive.Control
class={cn(
'flex h-4 w-4 shrink-0 items-center justify-center rounded border border-gray-300',
'ring-offset-white transition-colors',
'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:outline-none',
'disabled:cursor-not-allowed disabled:opacity-50',
'data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white',
'data-[state=indeterminate]:border-blue-600 data-[state=indeterminate]:bg-blue-600 data-[state=indeterminate]:text-white',
local.class,
)}
{...others}
<CheckboxPrimitive.Control
class={cn(
'flex h-4 w-4 shrink-0 items-center justify-center rounded border border-gray-300',
'ring-offset-white transition-colors',
'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:outline-none',
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
'data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white',
'data-[state=indeterminate]:border-blue-600 data-[state=indeterminate]:bg-blue-600 data-[state=indeterminate]:text-white',
local.class,
)}
{...others}
🤖 Prompt for AI Agents
In `@packages/web/src/components/ui/checkbox.tsx` around lines 71 - 81, The
disabled styles on CheckboxPrimitive.Control use the native CSS pseudo-class
("disabled:cursor-not-allowed disabled:opacity-50") which won't apply because
Control renders a wrapper element; change those to Ark UI's data-attribute
pattern to mirror the existing state styling (e.g. replace
"disabled:cursor-not-allowed disabled:opacity-50" with
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50" in the class
list for CheckboxPrimitive.Control so the wrapper responds to the component's
disabled state).

Comment on lines +100 to +108
<CheckboxPrimitive.Label
class={cn(
'text-sm leading-none font-medium text-gray-900',
'data-disabled:cursor-not-allowed data-disabled:opacity-70',
local.class,
)}
{...others}
/>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect Tailwind data attribute syntax - disabled styling won't apply.

data-disabled: is invalid Tailwind syntax. The correct arbitrary data attribute syntax requires brackets: data-[disabled]:. The disabled styling is currently non-functional.

Suggested fix
     <CheckboxPrimitive.Label
       class={cn(
         'text-sm leading-none font-medium text-gray-900',
-        'data-disabled:cursor-not-allowed data-disabled:opacity-70',
+        'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70',
         local.class,
       )}
       {...others}
     />
📝 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
<CheckboxPrimitive.Label
class={cn(
'text-sm leading-none font-medium text-gray-900',
'data-disabled:cursor-not-allowed data-disabled:opacity-70',
local.class,
)}
{...others}
/>
);
<CheckboxPrimitive.Label
class={cn(
'text-sm leading-none font-medium text-gray-900',
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70',
local.class,
)}
{...others}
/>
);
🤖 Prompt for AI Agents
In `@packages/web/src/components/ui/checkbox.tsx` around lines 100 - 108, The
Tailwind data attribute syntax used on CheckboxPrimitive.Label is
incorrect—replace the invalid `data-disabled:` usages with the proper arbitrary
attribute form `data-[disabled]:` so the disabled styles apply; update the class
construction in the CheckboxPrimitive.Label render (where cn is called with
'text-sm leading-none font-medium text-gray-900',
'data-disabled:cursor-not-allowed data-disabled:opacity-70', local.class, and
{...others}) to use 'data-[disabled]:cursor-not-allowed' and
'data-[disabled]:opacity-70' instead.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/web/src/components/ui/avatar.tsx`:
- Around line 87-92: The getInitials function can throw when name is
whitespace-only or contains empty segments; update getInitials to trim and split
then filter out empty parts (e.g., parts = name.trim().split('
').filter(Boolean)), return '' if parts.length === 0, and use safe access
(optional chaining or default '') when reading parts[0][0] and
parts[parts.length-1][0] so single-part and multi-part names both handle empty
strings without throwing; reference function name getInitials and the parts
variable when making the change.
🧹 Nitpick comments (2)
packages/web/src/components/admin/UserTable.jsx (1)

109-114: Consider removing redundant title attribute.

The title attribute on the <img> element (line 112) is redundant since the tooltip already provides hover text via TooltipContent. Having both can cause visual overlap on hover.

Proposed fix
                         <img
                           src={providerInfo?.icon}
                           alt={providerInfo?.name || provider}
-                          title={providerInfo?.name || provider}
                           class='h-4 w-4'
                         />
packages/web/src/components/project/overview-tab/OverviewTab.jsx (1)

174-197: Consider resetting pendingRemoveMember after successful removal.

The dialog closes on success, but pendingRemoveMember retains stale data. While not functionally problematic, resetting it maintains cleaner state hygiene.

Suggested change
       setRemoveDialogOpen(false);
+      setPendingRemoveMember(null);
     } catch (err) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6868551 and 3c81531.

📒 Files selected for processing (7)
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/admin/UserDetail.jsx
  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
🧰 Additional context used
📓 Path-based instructions (17)
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/src/**

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

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
**/*.{js,ts,jsx,tsx}

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

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/ui/avatar.tsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/project/overview-tab/AddMemberModal.jsx
  • packages/web/src/components/Navbar.jsx
  • packages/web/src/components/admin/UserDetail.jsx
packages/web/src/**/*.{ts,tsx}

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

packages/web/src/**/*.{ts,tsx}: For UI icons, use solid-icons library or SVGs only (never emojis)
Use import aliases from jsconfig.json as defined for the project
Use Ark UI component library (@ark-ui/solid) for UI components
Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)
Do not prop-drill application state in SolidJS components; import stores directly where needed
Do not destructure props in SolidJS; access props.field directly or wrap in a function: () => props.field
Components should receive at most 1-5 props (local config only, not shared state)
Use createMemo for derived values in SolidJS
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state; import stores directly

Use import aliases from jsconfig.json as defined in the project configuration

packages/web/src/**/*.{ts,tsx}: Import stores directly and never prop-drill application state in SolidJS components
Never destructure SolidJS props - use props.field or () => props.field to preserve reactivity

Files:

  • packages/web/src/components/ui/avatar.tsx
packages/web/src/components/**/*.{ts,tsx}

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

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/ui/avatar.tsx
packages/{web,landing}/src/**/*.{ts,tsx}

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

packages/{web,landing}/src/**/*.{ts,tsx}: For UI icons, use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) or SVGs only, never emojis
Ensure browser compatibility, with particular attention to Safari
Group related components in subdirectories with barrel exports
Use Ark UI components from @ark-ui/solid for UI components
Use solid-icons icon library for all icon needs
Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure SolidJS props - access props.field directly or wrap in function: () => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Use createStore for complex state objects in SolidJS
Use createMemo for derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state - import stores directly

Files:

  • packages/web/src/components/ui/avatar.tsx
🧠 Learnings (29)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use Ark UI component library (`ark-ui/solid`) for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Use required libraries for specific functionality: Zod for validation, Drizzle for database, Better-Auth for authentication, Ark UI for components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Group related components in subdirectories with barrel exports
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/components/**/*.{ts,tsx} : Group related components in subdirectories with barrel exports
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Show and For components for conditional and list rendering in SolidJS

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's Show component for conditional rendering instead of ternary operators

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/ui/avatar.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use Ark UI component library (`ark-ui/solid`) for UI components

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/ui/avatar.tsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `solid-icons` library for icons (e.g., `solid-icons/bi`, `solid-icons/fi`)

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/admin/UserDetail.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : For UI icons, use `solid-icons` library (e.g., `solid-icons/bi`, `solid-icons/fi`) or SVGs only, never emojis

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/admin/UserDetail.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `solid-icons` icon library for all icon needs

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
  • packages/web/src/components/admin/UserDetail.jsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : For UI icons, use `solid-icons` library or SVGs only (never emojis)

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/ui/avatar.tsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's For component for rendering lists instead of Array.map()

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities

Applied to files:

  • packages/web/src/components/admin/UserTable.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple, reactive values in SolidJS

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple reactive values in SolidJS components

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for computed/derived values that depend on reactive state in SolidJS

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Use getChecklistStatus utility from `@/lib/checklist-domain.js` to determine current checklist status before performing operations

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Import stores directly in components and use store read/write action pattern - read from store, write via actions store

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/AMSTAR2/checklist-compare.js} : Use compareChecklists utility from checklist-compare.js for comparing reviewer checklists

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/components/project-ui/**/*.{js,jsx} : Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
  • packages/web/src/components/admin/ProjectDetail.jsx
📚 Learning: 2026-01-01T23:32:23.488Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.488Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.{js,ts,jsx,tsx} : Use `requireOrgMembership` and `requireProjectAccess` middleware instead of manual membership checks for org-scoped routes

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:01:35.601Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.601Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Check user membership in project before allowing WebSocket connection in ProjectDoc

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/web/src/components/project/overview-tab/OverviewTab.jsx
📚 Learning: 2025-12-27T03:01:35.601Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.601Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/web/src/components/admin/ProjectDetail.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components

Applied to files:

  • packages/web/src/components/Navbar.jsx
🧬 Code graph analysis (6)
packages/web/src/components/admin/UserTable.jsx (3)
packages/web/src/components/ui/avatar.tsx (1)
  • UserAvatar (124-124)
packages/web/src/components/ui/tooltip.tsx (4)
  • Tooltip (136-136)
  • TooltipTrigger (137-137)
  • TooltipPositioner (138-138)
  • TooltipContent (139-139)
packages/web/src/components/mocks/SettingsMockCombined.jsx (1)
  • providerInfo (160-163)
packages/web/src/components/project/overview-tab/OverviewTab.jsx (4)
packages/web/src/components/ui/avatar.tsx (4)
  • Avatar (124-124)
  • AvatarImage (124-124)
  • AvatarFallback (124-124)
  • getInitials (124-124)
packages/web/src/components/ui/collapsible.tsx (4)
  • Collapsible (113-113)
  • CollapsibleTrigger (113-113)
  • CollapsibleIndicator (113-113)
  • CollapsibleContent (113-113)
packages/web/src/primitives/useProject/index.js (2)
  • studies (152-152)
  • members (154-154)
packages/web/src/components/ui/alert-dialog.tsx (11)
  • AlertDialog (270-270)
  • AlertDialogBackdrop (271-271)
  • AlertDialogPositioner (272-272)
  • AlertDialogContent (273-273)
  • AlertDialogHeader (274-274)
  • AlertDialogIcon (275-275)
  • AlertDialogTitle (276-276)
  • AlertDialogDescription (277-277)
  • AlertDialogFooter (278-278)
  • AlertDialogCancel (279-279)
  • AlertDialogAction (280-280)
packages/web/src/components/ui/avatar.tsx (1)
packages/ui/src/components/Avatar.tsx (1)
  • AvatarProps (8-21)
packages/web/src/components/admin/ProjectDetail.jsx (2)
packages/web/src/components/ui/avatar.tsx (1)
  • UserAvatar (124-124)
packages/web/src/components/ui/dialog.tsx (8)
  • Dialog (198-198)
  • DialogBackdrop (200-200)
  • DialogPositioner (201-201)
  • DialogContent (202-202)
  • DialogHeader (206-206)
  • DialogTitle (203-203)
  • DialogCloseTrigger (205-205)
  • DialogBody (207-207)
packages/web/src/components/Navbar.jsx (2)
packages/web/src/components/ui/avatar.tsx (4)
  • Avatar (124-124)
  • AvatarImage (124-124)
  • AvatarFallback (124-124)
  • getInitials (124-124)
packages/web/src/api/better-auth-store.js (1)
  • user (217-240)
packages/web/src/components/admin/UserDetail.jsx (2)
packages/web/src/components/ui/avatar.tsx (1)
  • UserAvatar (124-124)
packages/web/src/components/ui/dialog.tsx (8)
  • Dialog (198-198)
  • DialogBackdrop (200-200)
  • DialogPositioner (201-201)
  • DialogContent (202-202)
  • DialogHeader (206-206)
  • DialogTitle (203-203)
  • DialogCloseTrigger (205-205)
  • DialogBody (207-207)
⏰ 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). (1)
  • GitHub Check: Workers Builds: corates
🔇 Additional comments (20)
packages/web/src/components/project/overview-tab/AddMemberModal.jsx (2)

5-6: LGTM! Clean migration to modular Avatar composition.

The updated imports correctly source the Avatar primitives from the local UI module and maintain the @corates/ui import for Select and showToast. This aligns with the project's component architecture. Based on learnings, the use of Ark UI components from @ark-ui/solid (underlying the avatar module) is consistent with coding guidelines.


224-232: Avatar composition is well-structured.

The composition pattern using Avatar > AvatarImage > AvatarFallback provides proper fallback handling when user images fail to load. The getInitials helper correctly generates initials from the display name chain.

packages/web/src/components/ui/avatar.tsx (2)

1-31: Well-structured Ark UI integration with comprehensive documentation.

The module correctly imports from @ark-ui/solid/avatar and uses proper TypeScript type imports. The JSDoc examples provide clear usage patterns for consumers. Based on learnings, this follows the project's guidelines for using Ark UI components.


115-122: UserAvatar correctly accesses props without destructuring.

The convenience component properly accesses props.class, props.src, props.alt, and props.name directly, maintaining SolidJS reactivity. As per coding guidelines, this follows the pattern of not destructuring props in SolidJS components.

packages/web/src/components/Navbar.jsx (2)

7-7: Import migration aligns with the new avatar module.

The import correctly sources all needed Avatar primitives and the getInitials helper from the local UI module, replacing the previous @corates/ui import.


138-143: Avatar composition handles loading and offline states gracefully.

The fallback chain user()?.name || storedName ensures initials display correctly during auth loading states, preventing avatar flashing. The composition pattern with AvatarImage and AvatarFallback is consistent with the new UI module design.

packages/web/src/components/admin/UserTable.jsx (2)

9-15: Imports correctly updated for new UI primitives.

The migration to UserAvatar and the composed Tooltip structure (Tooltip, TooltipTrigger, TooltipPositioner, TooltipContent) follows the Ark UI patterns established in the project. Based on learnings, this aligns with the requirement to use Ark UI components.


55-59: UserAvatar simplifies avatar rendering.

Using UserAvatar here is appropriate since it's a straightforward user avatar with no custom fallback styling needed. This reduces boilerplate compared to the full composition pattern.

packages/web/src/components/admin/ProjectDetail.jsx (2)

32-43: Imports correctly updated for modular Dialog and Avatar components.

The migration includes all necessary Dialog primitives and the UserAvatar component, following the consistent pattern established across admin components.


522-580: Dialog composition provides consistent modal experience.

The confirm dialog correctly implements the modular structure with DialogBackdrop, DialogPositioner, DialogContent, DialogHeader, DialogTitle, DialogCloseTrigger, and DialogBody. The conditional title rendering based on dialog type and the action handling in the confirm button are well-structured.

packages/web/src/components/admin/UserDetail.jsx (4)

40-51: Comprehensive imports for the modular UI migration.

All necessary Dialog primitives and UserAvatar are correctly imported. The import grouping is logical with UI components separated from store and utility imports.


270-274: UserAvatar with custom sizing works well for the header.

The class='h-16 w-16 text-xl' prop correctly sizes the avatar for the user detail header. The fallback chain for src and name handles cases where avatarUrl or displayName might be missing.


646-690: Ban Dialog follows the modular composition pattern.

The dialog structure is consistent with other admin dialogs. The textarea for ban reason and the conditional loading state in the button provide good UX. The onOpenChange={setBanDialog} correctly handles closing via backdrop click or escape key.


693-741: Confirm Dialog handles multiple action types cleanly.

The dialog correctly uses conditional rendering with Show to display appropriate content for 'delete' vs 'revoke-all' actions. The confirm button handler properly dispatches to the correct action based on dialog type.

packages/web/src/components/project/overview-tab/OverviewTab.jsx (6)

15-35: LGTM!

The updated imports follow project conventions: Ark UI components for UI primitives and solid-icons for icons. The migration from inline/legacy patterns to the modular Avatar, Collapsible, and AlertDialog components is well-structured.


60-62: LGTM!

Good use of createSignal for local dialog state. The pattern of storing pending action data before showing the confirmation dialog is clean and maintainable.


345-359: LGTM!

The Avatar implementation correctly uses the new component API with proper fallback handling. The image source logic appropriately handles relative paths, absolute URLs, and the default avatar API endpoint.


427-442: Verify the CollapsibleTrigger layout is intentional.

The CollapsibleTrigger contains an h2 and a div as direct children without a flex container, causing them to stack vertically. If a side-by-side layout (title left, indicator right) was intended, add flex styling to the trigger.

If the current stacked layout is intentional (title above, helper text with indicator below), this can be ignored.

Optional fix for side-by-side layout
-            <CollapsibleTrigger class='cursor-pointer px-6 py-4 transition-colors select-none hover:bg-gray-50'>
+            <CollapsibleTrigger class='flex cursor-pointer items-center justify-between px-6 py-4 transition-colors select-none hover:bg-gray-50'>
               <h2 class='text-lg font-semibold text-gray-900'>Figures</h2>

446-461: Consistent implementation.

The Tables collapsible follows the same pattern as Figures. The same layout consideration noted above applies here.


471-498: LGTM!

The AlertDialog implementation is well-structured with proper component composition. The dynamic title and description based on self-removal vs. member removal provides good UX clarity. The danger variant is appropriately applied for the destructive action.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/web/src/components/ui/avatar.tsx
Copy link
Copy Markdown

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
packages/web/src/primitives/useProject/studies.js (1)

153-170: Use apiFetch for project update requests.

Raw fetch bypasses the shared error handling/toast flow and duplicates parsing. Please switch both rename and description updates to apiFetch (or handleFetchError if you must keep fetch). As per coding guidelines, use apiFetch for frontend API calls.

Proposed fix
-    const response = await fetch(`${API_BASE}/api/orgs/${orgId}/projects/${projectId}`, {
-      method: 'PUT',
-      credentials: 'include',
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ name: trimmed }),
-    });
-
-    if (!response.ok) {
-      let message = 'Failed to rename project';
-      try {
-        const errorBody = await response.json();
-        message = errorBody?.error || message;
-      } catch (err) {
-        // JSON parsing failed - use default message
-        console.debug('Could not parse error response:', err.message);
-      }
-      throw new Error(message);
-    }
+    await apiFetch(`${API_BASE}/api/orgs/${orgId}/projects/${projectId}`, {
+      method: 'PUT',
+      credentials: 'include',
+      body: { name: trimmed },
+    });
-    const response = await fetch(`${API_BASE}/api/orgs/${orgId}/projects/${projectId}`, {
-      method: 'PUT',
-      credentials: 'include',
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ description: trimmed || null }),
-    });
-
-    if (!response.ok) {
-      let message = 'Failed to update description';
-      try {
-        const errorBody = await response.json();
-        message = errorBody?.error || message;
-      } catch (err) {
-        // JSON parsing failed - use default message
-        console.debug('Could not parse error response:', err.message);
-      }
-      throw new Error(message);
-    }
+    await apiFetch(`${API_BASE}/api/orgs/${orgId}/projects/${projectId}`, {
+      method: 'PUT',
+      credentials: 'include',
+      body: { description: trimmed || null },
+    });

Also applies to: 204-221

🤖 Fix all issues with AI agents
In `@packages/web/src/components/project/CreateProjectForm.jsx`:
- Line 205: The placeholder text in CreateProjectForm.jsx currently reads
"SimpleSelect an organization" (a typo/search-replace artifact); update the
placeholder prop on the relevant input/select component (the element with
placeholder='SimpleSelect an organization') to the correct user-facing string,
e.g., "Select an organization", so the UI displays the proper prompt.

In `@packages/web/src/components/project/overview-tab/AddMemberModal.jsx`:
- Line 277: In AddMemberModal.jsx fix the typos: change the comment
"SimpleSelected User Display" to a clear label like "Selected User Display" and
update the user-facing placeholder that currently reads "SimpleSelect" to a
proper user prompt such as "Search users..." or "Select a user"; locate the JSX
block that renders the selected-user display and the input/Select control
(within the AddMemberModal component) and update the comment and the placeholder
prop accordingly so they are user-friendly and free of search-replace artifacts.

In `@packages/web/src/components/ui/select.tsx`:
- Around line 301-356: The collection() and disabledSet() functions inside
SimpleSelect recreate new instances each render causing mismatched references;
replace them with memos using createMemo (e.g., const disabledSet =
createMemo(() => new Set(local.disabledValues || [])) and const collection =
createMemo(() => createListCollection({...})) ) and update all usages to call
disabledSet() and collection() (or access .() per framework conventions) so the
Select prop and the <For> loop receive the same stable instances until inputs
change; ensure item mapping still uses disabledSet() inside the memo and
selectValue() remains unchanged.
♻️ Duplicate comments (2)
packages/web/src/components/ui/checkbox.tsx (2)

71-78: Use Tailwind data attribute variant syntax for disabled styles.

data-disabled: will not apply; use data-[disabled]: instead.

Suggested fix
-        'data-disabled:cursor-not-allowed data-disabled:opacity-50',
+        'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
Tailwind CSS arbitrary variants data attribute syntax data-[disabled]:

100-104: Fix Tailwind data attribute variant for label disabled styles.

data-disabled: is invalid; use data-[disabled]: so the styles apply.

Suggested fix
-        'data-disabled:cursor-not-allowed data-disabled:opacity-70',
+        'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70',
Tailwind CSS arbitrary variants data attribute syntax data-[disabled]:
🧹 Nitpick comments (7)
packages/web/src/components/checklist/LocalAppraisalsPanel.jsx (1)

52-59: Consider adding error handling for failed deletions.

If deleteChecklist throws or fails, the dialog still closes and state resets, which could leave users unaware that deletion failed. That said, this matches the pattern in LocalAppraisalsSection.jsx, so it may be intentional to handle errors at the store level.

Optional: Add error handling
 const confirmDelete = async () => {
   const checklistId = pendingDeleteId();
   if (checklistId) {
-    await deleteChecklist(checklistId);
+    try {
+      await deleteChecklist(checklistId);
+    } catch (error) {
+      // Error should be handled by store/apiFetch toast
+      console.error('Failed to delete checklist:', error);
+    }
   }
   setDeleteDialogOpen(false);
   setPendingDeleteId(null);
 };
packages/web/src/components/project/all-studies-tab/AssignReviewersModal.jsx (1)

35-35: Inconsistent signal/setter naming

The signal is named selectsReady but its setter is setSimpleSelectsReady. This inconsistency could cause confusion during maintenance. Consider aligning the names.

Suggested fix
-  const [selectsReady, setSimpleSelectsReady] = createSignal(false);
+  const [selectsReady, setSelectsReady] = createSignal(false);

Then update references at lines 82, 87, and 139 from setSimpleSelectsReady to setSelectsReady.

packages/web/src/components/project/ProjectView.jsx (1)

238-264: Consider memoizing tabDefinitions if tab counts change frequently.

tabDefinitions is recreated on every render. Since it contains getCount functions that access reactive state, this is functionally correct. However, if performance becomes a concern, consider wrapping it in createMemo to avoid recreating the array structure.

packages/web/src/primitives/useProject/studies.js (1)

185-187: Verify project list cache invalidation coverage.

If any views read project lists via queryKeys.projects.byOrg(orgId) or queryKeys.projects.list(userId), invalidating only queryKeys.projects.all can leave stale lists after rename/description updates. Please verify which key(s) are used and invalidate accordingly.

Also applies to: 236-237

packages/web/src/components/ui/password-input.tsx (1)

48-55: Avoid splitProps to comply with the no-prop-destructuring rule.

Current components split props and then use local/others. The guidelines prefer accessing props directly. Consider spreading props and overriding class/children instead, and apply the same pattern to the other subcomponents in this file. As per coding guidelines.

Proposed fix (apply similarly to other subcomponents)
-const PasswordInput: Component<PasswordInputProps> = props => {
-  const [local, others] = splitProps(props, ['class', 'children']);
-  return (
-    <PasswordInputPrimitive.Root class={cn('w-full', local.class)} {...others}>
-      {local.children}
-    </PasswordInputPrimitive.Root>
-  );
-};
+const PasswordInput: Component<PasswordInputProps> = props => (
+  <PasswordInputPrimitive.Root {...props} class={cn('w-full', props.class)}>
+    {props.children}
+  </PasswordInputPrimitive.Root>
+);
packages/web/src/components/project/CreateProjectForm.jsx (1)

32-32: Inconsistent setter naming pattern.

The setter setSimpleSelectedOrgId includes "Simple" in the name, which appears to be a search-replace artifact. Compare to DevImportProject.jsx at line 23 which uses the standard pattern setSelectedOrgId. Consider using consistent naming.

Suggested fix
-  const [selectedOrgId, setSimpleSelectedOrgId] = createSignal(null);
+  const [selectedOrgId, setSelectedOrgId] = createSignal(null);

And update usages at lines 41 and 204 accordingly.

Also applies to: 41-41

packages/web/src/components/project/overview-tab/AddMemberModal.jsx (1)

25-26: Inconsistent naming pattern from search-replace.

The signal setters and handler include "Simple" prefix (setSimpleSelectedUser, setSimpleSelectedRole, handleSimpleSelectUser) which appears to be a search-replace artifact. Standard naming would be setSelectedUser, setSelectedRole, handleSelectUser.

Suggested fix
-  const [selectedUser, setSimpleSelectedUser] = createSignal(null);
-  const [selectedRole, setSimpleSelectedRole] = createSignal('member');
+  const [selectedUser, setSelectedUser] = createSignal(null);
+  const [selectedRole, setSelectedRole] = createSignal('member');
-  const handleSimpleSelectUser = user => {
-    setSimpleSelectedUser(user);
+  const handleSelectUser = user => {
+    setSelectedUser(user);

Update all usages at lines 146-147, 222, 303, 322 accordingly.

Also applies to: 84-88

Comment thread packages/web/src/components/project/CreateProjectForm.jsx Outdated
</Show>

{/* Selected User Display */}
{/* SimpleSelected User Display */}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Typos in comment and user-facing placeholder text.

The comment at line 277 says "SimpleSelected" and the placeholder at line 323 contains "SimpleSelect" - both appear to be search-replace artifacts.

Suggested fix
-            {/* SimpleSelected User Display */}
+            {/* Selected User Display */}
-                placeholder='SimpleSelect a role'
+                placeholder='Select a role'

Also applies to: 323-323

🤖 Prompt for AI Agents
In `@packages/web/src/components/project/overview-tab/AddMemberModal.jsx` at line
277, In AddMemberModal.jsx fix the typos: change the comment "SimpleSelected
User Display" to a clear label like "Selected User Display" and update the
user-facing placeholder that currently reads "SimpleSelect" to a proper user
prompt such as "Search users..." or "Select a user"; locate the JSX block that
renders the selected-user display and the input/Select control (within the
AddMemberModal component) and update the comment and the placeholder prop
accordingly so they are user-friendly and free of search-replace artifacts.

Comment on lines +301 to +356
const SimpleSelect: Component<SimpleSelectProps> = props => {
const [local, others] = splitProps(props, [
'items',
'value',
'onChange',
'label',
'placeholder',
'disabledValues',
'inDialog',
'class',
]);

const disabledSet = () => new Set(local.disabledValues || []);

const collection = () =>
createListCollection({
items: (local.items || []).map(item => ({
...item,
disabled: item.disabled || disabledSet().has(item.value),
})),
itemToString: item => item.label,
itemToValue: item => item.value,
});

const selectValue = () => (local.value != null ? [local.value] : []);

return (
<Select
collection={collection()}
value={selectValue()}
onValueChange={values => local.onChange?.(values[0] || '')}
class={local.class}
{...others}
>
<Show when={local.label}>
<SelectLabel>{local.label}</SelectLabel>
</Show>
<SelectControl>
<SelectTrigger>
<SelectValueText placeholder={local.placeholder || 'Select option'} />
<SelectIndicator />
</SelectTrigger>
</SelectControl>
<SelectPositioner inDialog={local.inDialog}>
<SelectContent>
<For each={collection().items}>
{item => (
<SelectItem item={item}>
<SelectItemText>{item.label}</SelectItemText>
<SelectItemIndicator />
</SelectItem>
)}
</For>
</SelectContent>
</SelectPositioner>
<SelectHiddenSelect />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if this file exists and view the actual code around the lines mentioned
head -360 packages/web/src/components/ui/select.tsx | tail -80

Repository: InfinityBowman/corates

Length of output: 188


🏁 Script executed:

# Search for how Select component is used elsewhere to understand the pattern
rg "Select\s*collection=" packages/web/src --type tsx -A 2 -B 2

Repository: InfinityBowman/corates

Length of output: 188


🏁 Script executed:

# Check if there are other memoized collections in the codebase
rg "createMemo.*createListCollection" packages/web/src --type tsx

Repository: InfinityBowman/corates

Length of output: 188


🏁 Script executed:

# Look for Ark UI documentation or types to understand Select behavior
rg "@ark-ui.*Select" packages/web/src --type tsx | head -20

Repository: InfinityBowman/corates

Length of output: 188


🏁 Script executed:

# Check package.json for Ark UI version
cat package.json | grep -A 2 -B 2 "@ark-ui"

Repository: InfinityBowman/corates

Length of output: 188


Memoize the collection and disabled set to maintain stable references.

The collection() and disabledSet() functions create new instances each time they are called. Since collection() is invoked both in the Select component prop and in the For loop within the same render, the Select component and the item loop receive different collection objects, which can break Ark UI's selection state and item identity tracking. Wrap both in createMemo so the same instance is reused until dependencies change.

Proposed fix
-import { For, Show, splitProps } from 'solid-js';
+import { For, Show, splitProps, createMemo } from 'solid-js';
@@
-  const disabledSet = () => new Set(local.disabledValues || []);
+  const disabledSet = createMemo(() => new Set(local.disabledValues || []));
 
-  const collection = () =>
-    createListCollection({
-      items: (local.items || []).map(item => ({
-        ...item,
-        disabled: item.disabled || disabledSet().has(item.value),
-      })),
-      itemToString: item => item.label,
-      itemToValue: item => item.value,
-    });
+  const collection = createMemo(() =>
+    createListCollection({
+      items: (local.items || []).map(item => ({
+        ...item,
+        disabled: item.disabled || disabledSet().has(item.value),
+      })),
+      itemToString: item => item.label,
+      itemToValue: item => item.value,
+    }),
+  );
🤖 Prompt for AI Agents
In `@packages/web/src/components/ui/select.tsx` around lines 301 - 356, The
collection() and disabledSet() functions inside SimpleSelect recreate new
instances each render causing mismatched references; replace them with memos
using createMemo (e.g., const disabledSet = createMemo(() => new
Set(local.disabledValues || [])) and const collection = createMemo(() =>
createListCollection({...})) ) and update all usages to call disabledSet() and
collection() (or access .() per framework conventions) so the Select prop and
the <For> loop receive the same stable instances until inputs change; ensure
item mapping still uses disabledSet() inside the memo and selectValue() remains
unchanged.

Copy link
Copy Markdown

@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: 5

Caution

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

⚠️ Outside diff range comments (2)
packages/web/src/components/checklist/ChecklistYjsWrapper.jsx (1)

340-358: Guard confirmMarkComplete against stale state.
If the dialog stays open while the checklist or study becomes unavailable or locked, getNextStatusForCompletion can throw or you could overwrite a finalized status. Re-check before updating.

Proposed fix
-  const confirmMarkComplete = () => {
-    const study = currentStudy();
+  const confirmMarkComplete = () => {
+    const checklist = currentChecklist();
+    const study = currentStudy();
+    if (!checklist || !study || !isEditable(checklist.status)) {
+      setCompleteDialogOpen(false);
+      return;
+    }
     // Determine the appropriate status based on reviewer count
     const nextStatus = getNextStatusForCompletion(study);
     updateChecklist(params.studyId, params.checklistId, { status: nextStatus });
packages/web/src/components/project/ProjectView.jsx (1)

186-206: Pass tag parameter to importFromGoogleDrive and use useProject.addPdfToStudy operation.

The importFromGoogleDrive call is missing the required tag parameter. Per coding guidelines, pass the tag (primary or supplementary) as the fourth argument. Additionally, switch from projectActionsStore.pdf.addToStudy() to the addPdfToStudy operation from the useProject hook, which expects the tag field in the metadata object to maintain consistency with other PDF add operations and centralize PDF writes.

🤖 Fix all issues with AI agents
In `@packages/web/src/components/admin/StorageManagement.jsx`:
- Around line 423-427: The close button (DialogCloseTrigger with the FiX icon)
is missing an accessible name — add an accessible label by giving
DialogCloseTrigger an aria-label (e.g., aria-label="Close") or by including
visually hidden text inside the trigger (e.g., a <span
className="sr-only">Close</span>) so screen readers can announce the control;
update the element where DialogCloseTrigger and FiX are used to include one of
these accessible naming approaches.

In `@packages/web/src/components/admin/UserDetail.jsx`:
- Around line 650-654: The DialogCloseTrigger icon-only buttons (components
named DialogCloseTrigger with FiX icon) lack accessible labels; update both
instances (the one near DialogTitle "Ban User" and the other at lines ~701-703)
to include an accessible label by adding an aria-label like "Close dialog" or by
including visually hidden text inside the trigger; ensure the aria-label/value
is descriptive and matches the dialog context (e.g., "Close Ban User dialog") so
screen readers announce the button correctly.

In `@packages/web/src/components/auth/CompleteProfile.jsx`:
- Around line 537-546: The StepsCompletedContent block is unreachable because
handleSubmit immediately navigates to the dashboard before the Steps component
can render the completed state; either remove StepsCompletedContent or change
handleSubmit to set currentStep to the final index (e.g., call setCurrentStep(3)
or set currentStep to count) and then perform a delayed navigation (e.g.,
setTimeout) so the completed UI is visible briefly; locate the handleSubmit
function and the currentStep setter (e.g., setCurrentStep or setStep) and update
it accordingly or delete the StepsCompletedContent JSX to eliminate dead code.

In
`@packages/web/src/components/project/all-studies-tab/EditPdfMetadataModal.jsx`:
- Around line 127-191: The labels in EditPdfMetadataModal.jsx are not linked to
their inputs (Article Title, First Author, Publication Year, Journal, DOI): add
unique id attributes to each input/textarea (e.g., id="title", "firstAuthor",
"publicationYear", "journal", "doi") and set the corresponding label htmlFor to
the same id; keep existing value/onInput handlers (title()/setTitle,
firstAuthor()/setFirstAuthor, publicationYear()/setPublicationYear,
journal()/setJournal, doi()/setDoi) unchanged so accessibility is restored
without altering behavior.

In `@packages/web/src/components/settings/pages/LinkedAccountsSection.jsx`:
- Around line 376-380: The close icon (DialogCloseTrigger containing FiX) lacks
an accessible name; update the DialogCloseTrigger (and/or the element rendering
FiX) to include an accessible label such as aria-label or title (for example
aria-label={`Close ${unlinkProviderName()}`} or a generic "Close") so screen
readers can announce its purpose; ensure the attribute is placed on the
interactive element rendered by DialogCloseTrigger (not only on the icon) and
keep the label concise and localized if applicable.
♻️ Duplicate comments (1)
packages/web/src/components/project/overview-tab/AddMemberModal.jsx (1)

277-277: Fix the typo in the comment and placeholder text.

These are the same typos previously flagged; please update both to user-friendly text.

Also applies to: 323-323

🧹 Nitpick comments (10)
packages/web/src/components/ui/qr-code.tsx (1)

68-84: Consider removing unnecessary empty strings in cn() calls.

The cn('', local.class) pattern works but the empty string is redundant - cn handles undefined gracefully. This is purely cosmetic.

Optional cleanup
 const QRCodeFrame: Component<QRCodeFrameProps> = props => {
   const [local, others] = splitProps(props, ['class', 'children']);
   return (
-    <QrCodePrimitive.Frame class={cn('', local.class)} {...others}>
+    <QrCodePrimitive.Frame class={local.class} {...others}>
       {local.children}
     </QrCodePrimitive.Frame>
   );
 };
 
 // ...
 
 const QRCodePattern: Component<QRCodePatternProps> = props => {
   const [local, others] = splitProps(props, ['class']);
-  return <QrCodePrimitive.Pattern class={cn('', local.class)} {...others} />;
+  return <QrCodePrimitive.Pattern class={local.class} {...others} />;
 };
packages/web/src/components/project/add-studies/AddStudiesForm.jsx (1)

223-239: Compute tab count once per render.

getCount() is invoked twice per tab render. Store the value in a local const to avoid duplicate reactive reads and keep the JSX simpler.

Proposed change
                 {tab => {
                   const getCount = () => {
                     if (tab.value === 'pdfs') return studies.pdfCount();
                     if (tab.value === 'references') return studies.refCount();
                     if (tab.value === 'lookup') return studies.lookupCount();
                     if (tab.value === 'drive') return studies.driveCount();
                     return 0;
                   };
+                  const count = getCount();
                   return (
                     <TabsTrigger
                       value={tab.value}
                       class='gap-2 border-b-2 border-transparent text-gray-600 hover:text-gray-900 data-[selected]:border-blue-600 data-[selected]:text-gray-900'
                     >
                       {tab.label}
-                      <Show when={getCount() > 0}>
+                      <Show when={count > 0}>
                         <span class='inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-blue-100 px-1.5 text-xs font-medium text-blue-700'>
-                          {getCount()}
+                          {count}
                         </span>
                       </Show>
                     </TabsTrigger>
                   );
                 }}
packages/web/src/components/ui/pin-input.tsx (1)

109-116: Consider exporting prop types for consumer use.

The type definitions (PinInputProps, PinInputLabelProps, PinInputControlProps, PinInputFieldProps) are declared but not exported. Consumers building wrappers or typed integrations would benefit from having these types available.

Suggested export with types
 export {
   PinInput,
   PinInputLabel,
   PinInputControl,
   PinInputField,
   PinInputHiddenInput,
   PinInputContext,
 };
+
+export type {
+  PinInputProps,
+  PinInputLabelProps,
+  PinInputControlProps,
+  PinInputFieldProps,
+};
packages/web/src/components/auth/CompleteProfile.jsx (1)

238-242: Consider removing the arbitrary delay or documenting its purpose.

The 200ms delay before navigation appears to be a workaround, possibly to allow state updates to propagate. If this is necessary due to a timing issue with updateProfile, consider adding a brief comment explaining why, or investigate if it can be removed.

Suggestion: Add explanatory comment if delay is intentional
-      await new Promise(resolve => setTimeout(resolve, 200));
+      // Brief delay to allow profile state to propagate before navigation
+      await new Promise(resolve => setTimeout(resolve, 200));
packages/web/src/components/settings/pages/TwoFactorSetup.jsx (1)

331-340: Ensure autocomplete reaches the input element.

If PasswordInput does not forward autoComplete to the underlying input, the browser will ignore it. Consider placing autoComplete on PasswordInputField or confirm the wrapper forwards the prop.

Proposed change
-              <PasswordInput autoComplete='current-password'>
+              <PasswordInput>
                 <PasswordInputLabel>Password</PasswordInputLabel>
                 <PasswordInputControl>
                   <PasswordInputField
+                    autoComplete='current-password'
                     value={password()}
                     onInput={e => setPassword(e.target.value)}
                   />
                   <PasswordInputVisibilityTrigger />
                 </PasswordInputControl>
               </PasswordInput>
packages/web/src/components/settings/pages/MergeAccountsDialog.jsx (2)

170-188: Use shared error handler for consistency.

This path bypasses the shared error handling used elsewhere in the component, which can yield inconsistent messaging and missing error parsing. Consider reusing the same utility.

Proposed fix
-    } catch (err) {
-      setError(err.message);
-      setStep(STEPS.CONFIRM);
-    } finally {
+    } catch (err) {
+      const { handleError } = await import('@/lib/error-utils.js');
+      await handleError(err, {
+        setError,
+        showToast: false,
+      });
+      setStep(STEPS.CONFIRM);
+    } finally {
       setLoading(false);
     }

299-317: Prefer Show for conditional copy blocks.

The conditional instruction text is currently using a ternary in JSX. Align with the SolidJS guidance to use Show for conditional rendering in UI copy blocks. As per coding guidelines.

Proposed fix
-                <p class='text-sm text-gray-600'>
-                  {isOrcidConflict() ?
-                    `Enter the email address or ORCID ID (e.g., 0000-0001-2345-6789) of the other CoRATES account. We'll send a verification code to prove you own it.`
-                  : `Enter the email address of the other CoRATES account. We'll send a verification code to prove you own it.`
-                  }
-                </p>
+                <p class='text-sm text-gray-600'>
+                  <Show
+                    when={isOrcidConflict()}
+                    fallback={
+                      `Enter the email address of the other CoRATES account. We'll send a verification code to prove you own it.`
+                    }
+                  >
+                    {`Enter the email address or ORCID ID (e.g., 0000-0001-2345-6789) of the other CoRATES account. We'll send a verification code to prove you own it.`}
+                  </Show>
+                </p>
packages/web/src/components/ui/toast.tsx (2)

141-159: Consider using Show component for conditional icon rendering.

Per coding guidelines, SolidJS recommends using the Show component for conditional rendering instead of && patterns. While this works, Show is more idiomatic and avoids potential reactivity edge cases.

Additionally, the loading toast type has no icon rendered. Consider adding a spinner icon for visual consistency.

Suggested refactor using Show component
         {toast => (
           <ToastRoot>
             <div class='flex gap-3'>
-              {toast().type === 'success' && (
-                <FiCheckCircle class='h-5 w-5 shrink-0 text-green-500' />
-              )}
-              {toast().type === 'error' && <FiAlertCircle class='h-5 w-5 shrink-0 text-red-500' />}
-              {toast().type === 'warning' && (
-                <FiAlertTriangle class='h-5 w-5 shrink-0 text-amber-500' />
-              )}
-              {toast().type === 'info' && <FiInfo class='h-5 w-5 shrink-0 text-blue-500' />}
+              <Show when={toast().type === 'success'}>
+                <FiCheckCircle class='h-5 w-5 shrink-0 text-green-500' />
+              </Show>
+              <Show when={toast().type === 'error'}>
+                <FiAlertCircle class='h-5 w-5 shrink-0 text-red-500' />
+              </Show>
+              <Show when={toast().type === 'warning'}>
+                <FiAlertTriangle class='h-5 w-5 shrink-0 text-amber-500' />
+              </Show>
+              <Show when={toast().type === 'info'}>
+                <FiInfo class='h-5 w-5 shrink-0 text-blue-500' />
+              </Show>
+              <Show when={toast().type === 'loading'}>
+                <FiLoader class='h-5 w-5 shrink-0 animate-spin text-gray-500' />
+              </Show>
               <div class='flex-1'>

Note: You'll need to import FiLoader from solid-icons/fi and Show from solid-js.


165-196: Minor API inconsistency: ToastOptions interface not used by main methods.

The ToastOptions interface is defined but only used by update(). The other methods (success, error, etc.) use positional parameters. This is fine for simplicity, but consider unifying if the API expands.

packages/web/src/components/admin/UserDetail.jsx (1)

698-700: Prefer Show over a ternary for the dialog title.

Use Show for conditional rendering to match project guidelines. As per coding guidelines, use Show instead of ternary operators.

Proposed refactor
-                <DialogTitle>
-                  {confirmDialog()?.type === 'delete' ? 'Delete User' : 'Revoke All Sessions'}
-                </DialogTitle>
+                <DialogTitle>
+                  <Show when={confirmDialog()?.type === 'delete'} fallback='Revoke All Sessions'>
+                    Delete User
+                  </Show>
+                </DialogTitle>

Comment thread packages/web/src/components/admin/StorageManagement.jsx
Comment on lines +650 to +654
<DialogHeader>
<DialogTitle>Ban User</DialogTitle>
<DialogCloseTrigger>
<FiX class='h-5 w-5' />
</DialogCloseTrigger>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add an accessible label for the icon-only close triggers.

Without a label, screen readers will announce an unlabeled button. Consider adding an aria-label (or visually hidden text) to both DialogCloseTrigger instances.

Proposed fix
-                <DialogCloseTrigger>
+                <DialogCloseTrigger aria-label='Close dialog'>
                   <FiX class='h-5 w-5' />
                 </DialogCloseTrigger>

Also applies to: 701-703

🤖 Prompt for AI Agents
In `@packages/web/src/components/admin/UserDetail.jsx` around lines 650 - 654, The
DialogCloseTrigger icon-only buttons (components named DialogCloseTrigger with
FiX icon) lack accessible labels; update both instances (the one near
DialogTitle "Ban User" and the other at lines ~701-703) to include an accessible
label by adding an aria-label like "Close dialog" or by including visually
hidden text inside the trigger; ensure the aria-label/value is descriptive and
matches the dialog context (e.g., "Close Ban User dialog") so screen readers
announce the button correctly.

Comment on lines +537 to +546
{/* Completed Content (shows after all steps) */}
<StepsCompletedContent>
<div class='flex flex-col items-center justify-center py-8'>
<div class='mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100'>
<FiCheck class='h-8 w-8 text-green-600' />
</div>
<h2 class='mb-2 text-xl font-bold text-gray-900'>All Done!</h2>
<p class='text-sm text-gray-500'>Redirecting to your dashboard...</p>
</div>
</StepsCompletedContent>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

StepsCompletedContent appears unreachable.

This completion UI will never be displayed because handleSubmit navigates to the dashboard (line 242 or 230) before currentStep could reach 3. The Steps component shows StepsCompletedContent when step >= count, but navigation occurs immediately after successful submission.

Either:

  1. Remove this dead code if the immediate redirect is intentional
  2. Show the completed state briefly before navigating by setting currentStep(3) and using a delayed navigation
Option 1: Remove unreachable code
-            {/* Completed Content (shows after all steps) */}
-            <StepsCompletedContent>
-              <div class='flex flex-col items-center justify-center py-8'>
-                <div class='mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100'>
-                  <FiCheck class='h-8 w-8 text-green-600' />
-                </div>
-                <h2 class='mb-2 text-xl font-bold text-gray-900'>All Done!</h2>
-                <p class='text-sm text-gray-500'>Redirecting to your dashboard...</p>
-              </div>
-            </StepsCompletedContent>
Option 2: Show completion state before redirect (in handleSubmit)
+      // Show completion state before navigating
+      setCurrentStep(3);
       await new Promise(resolve => setTimeout(resolve, 200));
+      await new Promise(resolve => setTimeout(resolve, 1000)); // Show "All Done!" briefly
       navigate('/dashboard', { replace: true });
📝 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
{/* Completed Content (shows after all steps) */}
<StepsCompletedContent>
<div class='flex flex-col items-center justify-center py-8'>
<div class='mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100'>
<FiCheck class='h-8 w-8 text-green-600' />
</div>
<h2 class='mb-2 text-xl font-bold text-gray-900'>All Done!</h2>
<p class='text-sm text-gray-500'>Redirecting to your dashboard...</p>
</div>
</StepsCompletedContent>
{/* Content for each step */}
🤖 Prompt for AI Agents
In `@packages/web/src/components/auth/CompleteProfile.jsx` around lines 537 - 546,
The StepsCompletedContent block is unreachable because handleSubmit immediately
navigates to the dashboard before the Steps component can render the completed
state; either remove StepsCompletedContent or change handleSubmit to set
currentStep to the final index (e.g., call setCurrentStep(3) or set currentStep
to count) and then perform a delayed navigation (e.g., setTimeout) so the
completed UI is visible briefly; locate the handleSubmit function and the
currentStep setter (e.g., setCurrentStep or setStep) and update it accordingly
or delete the StepsCompletedContent JSX to eliminate dead code.

Comment on lines +127 to +191
{/* Article Title */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Article Title</label>
<textarea
value={title()}
onInput={e => setTitle(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='Full article title'
rows={2}
/>
</div>

{/* Author and Year */}
<div class='grid grid-cols-2 gap-4'>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>First Author</label>
<input
type='text'
value={firstAuthor()}
onInput={e => setFirstAuthor(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Smith'
/>
</div>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Publication Year</label>
<input
type='number'
value={publicationYear()}
onInput={e => setPublicationYear(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 2024'
min='1900'
max='2100'
step='1'
inputMode='numeric'
/>
</div>
</div>
{/* Author and Year */}
<div class='grid grid-cols-2 gap-4'>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>First Author</label>
<input
type='text'
value={firstAuthor()}
onInput={e => setFirstAuthor(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Smith'
/>
</div>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>
Publication Year
</label>
<input
type='number'
value={publicationYear()}
onInput={e => setPublicationYear(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 2024'
min='1900'
max='2100'
step='1'
inputMode='numeric'
/>
</div>
</div>

{/* Journal */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Journal</label>
<input
type='text'
value={journal()}
onInput={e => setJournal(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Journal of Clinical Research'
/>
</div>
{/* Journal */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Journal</label>
<input
type='text'
value={journal()}
onInput={e => setJournal(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Journal of Clinical Research'
/>
</div>

{/* DOI */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>DOI</label>
<input
type='text'
value={doi()}
onInput={e => setDoi(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 10.1000/xyz123'
/>
</div>
{/* DOI */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>DOI</label>
<input
type='text'
value={doi()}
onInput={e => setDoi(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 10.1000/xyz123'
/>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Form labels are not semantically associated with inputs.

The <label> elements lack htmlFor attributes and inputs lack id attributes, so screen readers won't announce the labels when users focus on inputs. This affects accessibility for users relying on assistive technology.

Suggested fix for one field (apply same pattern to all)
               {/* Article Title */}
               <div>
-                <label class='mb-1 block text-sm font-medium text-gray-700'>Article Title</label>
+                <label htmlFor='pdf-title' class='mb-1 block text-sm font-medium text-gray-700'>Article Title</label>
                 <textarea
+                  id='pdf-title'
                   value={title()}
                   onInput={e => setTitle(e.target.value)}

Apply the same pattern to firstAuthor, publicationYear, journal, and doi inputs.

📝 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
{/* Article Title */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Article Title</label>
<textarea
value={title()}
onInput={e => setTitle(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='Full article title'
rows={2}
/>
</div>
{/* Author and Year */}
<div class='grid grid-cols-2 gap-4'>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>First Author</label>
<input
type='text'
value={firstAuthor()}
onInput={e => setFirstAuthor(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Smith'
/>
</div>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Publication Year</label>
<input
type='number'
value={publicationYear()}
onInput={e => setPublicationYear(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 2024'
min='1900'
max='2100'
step='1'
inputMode='numeric'
/>
</div>
</div>
{/* Author and Year */}
<div class='grid grid-cols-2 gap-4'>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>First Author</label>
<input
type='text'
value={firstAuthor()}
onInput={e => setFirstAuthor(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Smith'
/>
</div>
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>
Publication Year
</label>
<input
type='number'
value={publicationYear()}
onInput={e => setPublicationYear(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 2024'
min='1900'
max='2100'
step='1'
inputMode='numeric'
/>
</div>
</div>
{/* Journal */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Journal</label>
<input
type='text'
value={journal()}
onInput={e => setJournal(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Journal of Clinical Research'
/>
</div>
{/* Journal */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>Journal</label>
<input
type='text'
value={journal()}
onInput={e => setJournal(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Journal of Clinical Research'
/>
</div>
{/* DOI */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>DOI</label>
<input
type='text'
value={doi()}
onInput={e => setDoi(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 10.1000/xyz123'
/>
</div>
{/* DOI */}
<div>
<label class='mb-1 block text-sm font-medium text-gray-700'>DOI</label>
<input
type='text'
value={doi()}
onInput={e => setDoi(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 10.1000/xyz123'
/>
</div>
{/* Article Title */}
<div>
<label htmlFor='pdf-title' class='mb-1 block text-sm font-medium text-gray-700'>Article Title</label>
<textarea
id='pdf-title'
value={title()}
onInput={e => setTitle(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='Full article title'
rows={2}
/>
</div>
{/* Author and Year */}
<div class='grid grid-cols-2 gap-4'>
<div>
<label htmlFor='pdf-first-author' class='mb-1 block text-sm font-medium text-gray-700'>First Author</label>
<input
id='pdf-first-author'
type='text'
value={firstAuthor()}
onInput={e => setFirstAuthor(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Smith'
/>
</div>
<div>
<label htmlFor='pdf-publication-year' class='mb-1 block text-sm font-medium text-gray-700'>
Publication Year
</label>
<input
id='pdf-publication-year'
type='number'
value={publicationYear()}
onInput={e => setPublicationYear(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 2024'
min='1900'
max='2100'
step='1'
inputMode='numeric'
/>
</div>
</div>
{/* Journal */}
<div>
<label htmlFor='pdf-journal' class='mb-1 block text-sm font-medium text-gray-700'>Journal</label>
<input
id='pdf-journal'
type='text'
value={journal()}
onInput={e => setJournal(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., Journal of Clinical Research'
/>
</div>
{/* DOI */}
<div>
<label htmlFor='pdf-doi' class='mb-1 block text-sm font-medium text-gray-700'>DOI</label>
<input
id='pdf-doi'
type='text'
value={doi()}
onInput={e => setDoi(e.target.value)}
class='w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
placeholder='e.g., 10.1000/xyz123'
/>
</div>
🤖 Prompt for AI Agents
In `@packages/web/src/components/project/all-studies-tab/EditPdfMetadataModal.jsx`
around lines 127 - 191, The labels in EditPdfMetadataModal.jsx are not linked to
their inputs (Article Title, First Author, Publication Year, Journal, DOI): add
unique id attributes to each input/textarea (e.g., id="title", "firstAuthor",
"publicationYear", "journal", "doi") and set the corresponding label htmlFor to
the same id; keep existing value/onInput handlers (title()/setTitle,
firstAuthor()/setFirstAuthor, publicationYear()/setPublicationYear,
journal()/setJournal, doi()/setDoi) unchanged so accessibility is restored
without altering behavior.

@InfinityBowman InfinityBowman merged commit a8b5138 into main Jan 18, 2026
1 of 2 checks passed
@InfinityBowman InfinityBowman deleted the 273-detailed-user-profile-creation branch January 18, 2026 20:52
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/web/src/components/admin/StorageManagement.jsx`:
- Around line 419-453: The dialog currently allows closing via backdrop or close
trigger while deletion is in progress; update the Dialog open/onOpenChange and
the close trigger behavior so the modal cannot be dismissed when loading() is
true: in the Dialog component, change the onOpenChange handler to ignore close
requests if loading() is true and only clear setDeleteDialog(null) when not
loading, and also disable or no-op the DialogCloseTrigger/click handler when
loading() is true (and similarly prevent backdrop clicks) so that handleDelete
can complete without the UI appearing to cancel the operation.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89a139b and ce43844.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
🧰 Additional context used
📓 Path-based instructions (14)
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

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

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/web/src/**

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

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
**/*.{js,ts,jsx,tsx}

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

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use Ark UI component library (`ark-ui/solid`) for UI components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `solid-icons` icon library for all icon needs
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `solid-icons` library for icons (e.g., `solid-icons/bi`, `solid-icons/fi`)
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Use required libraries for specific functionality: Zod for validation, Drizzle for database, Better-Auth for authentication, Ark UI for components
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Do not prop-drill application state in SolidJS components; import stores directly where needed
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/components/project-ui/**/*.{js,jsx} : Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `solid-icons` library for icons (e.g., `solid-icons/bi`, `solid-icons/fi`)

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `solid-icons` icon library for all icon needs

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : For UI icons, use `solid-icons` library (e.g., `solid-icons/bi`, `solid-icons/fi`) or SVGs only, never emojis

Applied to files:

  • packages/web/src/components/admin/StorageManagement.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Show and For components for conditional and list rendering in SolidJS

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple reactive values in SolidJS components

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple, reactive values in SolidJS

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's Show component for conditional rendering instead of ternary operators

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for computed/derived values that depend on reactive state in SolidJS

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/settings/pages/LinkedAccountsSection.jsx
🧬 Code graph analysis (1)
packages/web/src/components/admin/StorageManagement.jsx (1)
packages/web/src/components/ui/dialog.tsx (8)
  • Dialog (198-198)
  • DialogBackdrop (200-200)
  • DialogPositioner (201-201)
  • DialogContent (202-202)
  • DialogHeader (206-206)
  • DialogTitle (203-203)
  • DialogCloseTrigger (205-205)
  • DialogBody (207-207)
⏰ 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). (1)
  • GitHub Check: Workers Builds: corates
🔇 Additional comments (3)
packages/web/src/components/settings/pages/LinkedAccountsSection.jsx (2)

14-28: LGTM - Dialog imports migrated to local Ark UI components.

The FiX icon import from solid-icons/fi and the composed dialog imports from @/components/ui/dialog align with project conventions. Based on learnings, this follows the pattern of using Ark UI components for the UI library.


373-419: LGTM - Dialog refactored to composed structure with proper accessibility.

The dialog migration to the composed Ark UI pattern (DialogBackdrop, DialogPositioner, DialogContent, DialogHeader, DialogTitle, DialogBody, DialogCloseTrigger) is well-structured and consistent with the patterns used elsewhere in the codebase. The accessibility concern from the previous review has been addressed with aria-label='Close' on the DialogCloseTrigger.

packages/web/src/components/admin/StorageManagement.jsx (1)

16-30: Imports align with the new dialog composition.

The updated icon and dialog imports look consistent with the new UI primitives.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +419 to +453
<Dialog open={!!deleteDialog()} onOpenChange={open => !open && setDeleteDialog(null)}>
<DialogBackdrop />
<DialogPositioner>
<DialogContent class='max-w-md'>
<DialogHeader>
<DialogTitle>Delete Documents</DialogTitle>
<DialogCloseTrigger aria-label='Close'>
<FiX class='h-5 w-5' />
</DialogCloseTrigger>
</DialogHeader>
<DialogBody>
<div class='space-y-4'>
<p class='text-sm text-gray-600'>
Are you sure you want to delete {deleteDialog()?.length || 0} document
{deleteDialog()?.length === 1 ? '' : 's'}? This action cannot be undone.
</p>
<div class='flex justify-end space-x-3'>
<button
onClick={() => setDeleteDialog(null)}
disabled={loading()}
class='rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 disabled:opacity-50'
>
Cancel
</button>
<button
onClick={handleDelete}
disabled={loading()}
class='rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 focus:ring-2 focus:ring-blue-500 focus:outline-none disabled:opacity-50'
>
{loading() ? 'Deleting...' : 'Delete'}
</button>
</div>
</div>
</DialogBody>
</DialogContent>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prevent dialog dismissal while deletion is in progress.

Closing via the close trigger or backdrop while loading() is true can make users think the delete was canceled even though it continues. Consider blocking close while loading.

Proposed change
-      <Dialog open={!!deleteDialog()} onOpenChange={open => !open && setDeleteDialog(null)}>
+      <Dialog
+        open={!!deleteDialog()}
+        onOpenChange={open => {
+          if (loading()) return;
+          if (!open) setDeleteDialog(null);
+        }}
+      >
🤖 Prompt for AI Agents
In `@packages/web/src/components/admin/StorageManagement.jsx` around lines 419 -
453, The dialog currently allows closing via backdrop or close trigger while
deletion is in progress; update the Dialog open/onOpenChange and the close
trigger behavior so the modal cannot be dismissed when loading() is true: in the
Dialog component, change the onOpenChange handler to ignore close requests if
loading() is true and only clear setDeleteDialog(null) when not loading, and
also disable or no-op the DialogCloseTrigger/click handler when loading() is
true (and similarly prevent backdrop clicks) so that handleDelete can complete
without the UI appearing to cancel the operation.

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.

Detailed user profile creation

2 participants