226 better billing page#227
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthroughThis PR introduces a comprehensive billing UI redesign and new loading components. It adds Spinner, PageLoader, LoadingPlaceholder, and ButtonSpinner components to the UI package; refactors ProtectedGuard to use PageLoader; redesigns billing views (BillingPage, PricingTable, SubscriptionCard, InvoicesList, UsageCard) with rich features; adds members API fetching via new endpoint and hook; and includes minor formatting adjustments. Changes
Sequence DiagramsequenceDiagram
participant User
participant ProtectedGuard
participant BillingPage
participant TanStackQuery
participant API as Web API
participant Worker as Backend
participant DB as Database
User->>ProtectedGuard: Navigate to protected route
ProtectedGuard->>ProtectedGuard: Check isLoggedIn()
alt Not Logged In
ProtectedGuard->>ProtectedGuard: Show PageLoader
else Logged In
ProtectedGuard->>BillingPage: Render component
activate BillingPage
Note over BillingPage: Initialize data fetching
BillingPage->>TanStackQuery: useSubscription()
BillingPage->>TanStackQuery: useMembers()
par Fetch Subscription
TanStackQuery->>API: getSubscription()
API->>Worker: GET /api/billing/subscription
Worker->>DB: Query subscription data
DB-->>Worker: Subscription record
Worker-->>API: JSON response
API-->>TanStackQuery: subscription object
and Fetch Members
TanStackQuery->>API: getMembers()
API->>Worker: GET /api/billing/members
Worker->>DB: Query org members
DB-->>Worker: Members list + count
Worker-->>API: JSON response
API-->>TanStackQuery: members array + count
end
TanStackQuery-->>BillingPage: Data loaded
Note over BillingPage: Compute usage metrics<br/>from subscription quotas
BillingPage->>BillingPage: Render multi-section layout
BillingPage->>BillingPage: Show SubscriptionCard<br/>+ UsageCard<br/>+ InvoicesList
BillingPage-->>User: Display billing dashboard
deactivate BillingPage
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related issues
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 7dcd75a | Commit Preview URL | Jan 05 2026, 06:40 PM |
There was a problem hiding this comment.
Actionable comments posted: 8
Fix all issues with AI Agents 🤖
In @packages/ui/src/components/Spinner.tsx:
- Around line 65-81: The Spinner component currently hardcodes
'border-t-blue-200' which forces a blue spinner segment regardless of the
selected variant; update the component to use a variant-specific track color
instead (e.g., create a trackColorClasses mapping or extend variantClasses to
include the proper 'border-t-...' class) and replace the hardcoded
border-t-blue-200 with trackColorClasses[variant()] (or equivalent) so the
spinner's top border color matches the chosen variant while keeping existing
sizeClasses, borderWidthClasses, and local.class intact.
- Around line 132-141: The fallback div in the Spinner component uses an invalid
Tailwind class `min-h-50`; update that class to a valid utility (e.g.,
`min-h-48` or `min-h-52`) or use arbitrary value syntax like `min-h-[200px]` in
the JSX returned by the Show fallback to ensure the min-height is applied
correctly (locate the fallback div inside the Show in Spinner.tsx where
`fallback={<div class='flex min-h-50 w-full items-center
justify-center'>{content}</div>}` is rendered).
In @packages/web/src/components/billing/BillingPage.jsx:
- Around line 217-222: The Contact support anchor in BillingPage.jsx currently
uses a placeholder href ('#') which is non-functional; update the anchor in the
BillingPage component to use the same LANDING_URL pattern used by the
QuickAction above (replace href='#' with the LANDING_URL constant or computed
link) or remove the anchor until a real destination exists; locate the anchor
element rendering "Contact support" and swap its href to LANDING_URL (or delete
the <a> and render plain text) and ensure you import or reference the existing
LANDING_URL symbol consistently with QuickAction.
In @packages/web/src/components/billing/BillingPlansPage.jsx:
- Line 156: In BillingPlansPage.jsx update the div that currently uses the
invalid Tailwind class 'bg-linear-to-r' (the div with class='mt-16 rounded-2xl
bg-linear-to-r from-blue-600 to-blue-500 px-8 py-12 text-center') to use the
correct Tailwind gradient utility 'bg-gradient-to-r' so the gradient styles
(from-blue-600 to-blue-500) apply correctly.
In @packages/web/src/components/billing/InvoicesList.jsx:
- Around line 144-151: The "View all" button inside the Show wrapper renders
with no onClick and does nothing; remove the button block (the <button> element
inside the Show that checks invoices()?.invoices?.length) until the feature is
implemented, or alternatively add an onClick that performs a real action (e.g.,
call a navigation function like navigate('/billing/invoices') or toggle a state
handler such as setShowAll(true)); update any references to the Show wrapper
accordingly so there are no unused UI fragments.
In @packages/web/src/components/billing/UsageCard.jsx:
- Around line 51-53: The Tailwind class used for the gradient is incorrect:
replace the `bg-linear-to-r` class on the div inside the Show block (the element
rendered when isUnlimited() is true) with `bg-gradient-to-r`; keep the rest of
the class string (`h-2 w-full rounded-full from-green-100 to-green-200`)
unchanged so the gradient renders correctly.
In @packages/web/src/primitives/useSubscription.js:
- Line 70: Remove the debug console.log in useSubscription: delete the line
calling console.log(subscription()) inside the useSubscription hook so
subscription details are not printed on every render; search for the
useSubscription function and ensure no other transient debug console statements
remain (do not replace with logging of sensitive data).
In @packages/workers/src/routes/billing/index.js:
- Around line 143-154: The code makes a redundant DB count query
(memberCountResult / memberCount) after fetching the full members array
(result.members) which is inefficient and can lead to inconsistencies; replace
the separate count query by deriving the count from result.members (e.g., use
result.members?.length or 0) and return that as the count in the JSON response,
removing the memberCountResult/memberCount database query and references to it;
keep the returned members array as result.members || [] to match the existing
pattern.
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/billing/BillingPage.jsx (1)
104-106: Refetch runs on every render when checkout is successful.This
ifstatement executes on every component re-render whencheckoutSuccess()is true, potentially causing repeated API calls. Wrap it increateEffectto ensure it only runs once when the condition changes.Proposed fix
+import { Show, createSignal, createEffect } from 'solid-js'; ... - // Refetch subscription on successful checkout - if (checkoutSuccess()) { - refetch(); - } + // Refetch subscription on successful checkout + createEffect(() => { + if (checkoutSuccess()) { + refetch(); + } + });
🧹 Nitpick comments (7)
packages/web/src/components/billing/PricingTable.jsx (2)
28-36: Remove commented-out dead code.This commented code block appears to be leftover from development/debugging. Per best practices, dead code should be removed rather than left commented.
Proposed fix
const currentTier = () => props.currentTier ?? 'free'; - // const plans = () => adjust(defaultPlans(), 2, defaultPlans().plans.length - 1); - - // function adjust(arr, from, to) { - // const copy = [...arr.plans]; - // const [item] = copy.splice(from, 1); - // copy.splice(to, 0, item); - // let trial = copy.splice(1, 1); - // return { ...arr, plans: copy }; - // }
241-260: Consider usingButtonSpinnerfrom@corates/uifor consistency.This PR introduces a
ButtonSpinnercomponent in the UI package. Using it here instead of an inline SVG would improve consistency across the codebase.Proposed refactor
+import { ButtonSpinner } from '@corates/ui'; ... <Show when={loadingTier() === plan.tier} fallback={getButtonText(plan)}> - <span class='flex items-center justify-center gap-2'> - <svg class='h-4 w-4 animate-spin' fill='none' viewBox='0 0 24 24'> - <circle - class='opacity-25' - cx='12' - cy='12' - r='10' - stroke='currentColor' - stroke-width='4' - /> - <path - class='opacity-75' - fill='currentColor' - d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z' - /> - </svg> - Processing... - </span> + <ButtonSpinner label="Processing..." /> </Show>packages/workers/src/routes/billing/index.js (2)
114-124: Redundant dynamic imports -memberandeqare already imported at the top of the file.Lines 10-11 already import
memberfrom schema andeqfrom drizzle-orm. These dynamic imports are unnecessary and add overhead.Proposed fix
// If no active org in session, get user's first org if (!orgId) { - const { member } = await import('../../db/schema.js'); - const { eq } = await import('drizzle-orm'); const firstMembership = await db .select({ organizationId: member.organizationId }) .from(member) .where(eq(member.userId, user.id)) .limit(1) .get(); orgId = firstMembership?.organizationId; }
105-131: Consider extracting orgId resolution to a shared helper.The pattern for resolving
orgIdfrom session or first membership is duplicated across multiple routes in this file (/subscription,/members,/checkout,/portal,/single-project/checkout,/trial/start). Extracting this to a helper function would reduce duplication and ensure consistent behavior.Example helper:
async function resolveOrgIdFromSession(db, session, userId) { let orgId = session?.activeOrganizationId; if (!orgId) { const firstMembership = await db .select({ organizationId: member.organizationId }) .from(member) .where(eq(member.userId, userId)) .limit(1) .get(); orgId = firstMembership?.organizationId; } return orgId; }packages/web/src/components/billing/InvoicesList.jsx (1)
43-48: Currency is hardcoded to USD.The
formatAmountfunction assumes USD currency. If invoices can be in different currencies, consider extracting the currency from the invoice object.-function formatAmount(amount) { +function formatAmount(amount, currency = 'USD') { return new Intl.NumberFormat('en-US', { style: 'currency', - currency: 'USD', + currency, }).format(amount); }Then use:
formatAmount(invoice.amount, invoice.currency)packages/web/src/components/billing/SubscriptionCard.jsx (2)
188-213: Consider using the new Spinner from@corates/uiinstead of inline SVG.This PR introduces
SpinnerandButtonSpinnercomponents in@corates/ui. The inline SVG spinner here duplicates that functionality. As per coding guidelines, UI components should be imported from@corates/ui.Proposed refactor using ButtonSpinner
+import { ButtonSpinner } from '@corates/ui'; ... <button type='button' class='inline-flex flex-1 items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50' onClick={() => props.onManage?.()} disabled={props.manageLoading} > - <Show - when={!props.manageLoading} - fallback={ - <> - <svg class='h-4 w-4 animate-spin' fill='none' viewBox='0 0 24 24'> - <circle - class='opacity-25' - cx='12' - cy='12' - r='10' - stroke='currentColor' - stroke-width='4' - /> - <path - class='opacity-75' - fill='currentColor' - d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z' - /> - </svg> - Loading... - </> - } - > + <ButtonSpinner loading={props.manageLoading} size='sm' variant='secondary'> <FiCreditCard class='h-4 w-4' /> Manage Billing - </Show> + </ButtonSpinner> </button>
160-164: Consider a fallback for members without names.If
member.user?.nameis undefined, the span will render empty. A fallback like the user's email or a placeholder text would improve UX.Proposed fix
<For each={members()}> - {member => <span class='text-gray-500'>{member.user?.name}</span>} + {member => ( + <span class='text-gray-500'> + {member.user?.name || member.user?.email || 'Unknown member'} + </span> + )} </For>
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
packages/ui/src/components/Spinner.tsxpackages/ui/src/components/index.tspackages/web/src/api/billing.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/components/billing/InvoicesList.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/primitives/useMembers.jspackages/web/src/primitives/useSubscription.jspackages/workers/migrations/meta/0000_snapshot.jsonpackages/workers/migrations/meta/_journal.jsonpackages/workers/src/routes/billing/index.js
🧰 Additional context used
📓 Path-based instructions (21)
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
packages/web/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always usehandleFetchErrorfrom@/lib/error-utils.jsfor frontend fetch calls with optionalshowToastparameter
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor handling form validation errors, field-level errors, and global errors in frontend forms
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
packages/{web,workers}/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
UseisErrorCodeutility from@corates/sharedor@/lib/error-utils.jsto check for specific error codes instead of manual error comparisons
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
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/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)
packages/web/src/**/*.{js,jsx,ts,tsx}: Always useuploadPdffrom@api/pdf-api.jsfor 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)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor 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
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook instead of bypassing through direct API calls
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.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
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local components directories
Use solid-icons library for icons instead of emojis or other icon sources
Use Tailwind CSS classes for styling UI components
Apply responsive design using mobile-first approach with Tailwind CSS
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/ui/src/components/index.tspackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
packages/web/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Use import aliases from jsconfig.json (e.g., @/, @components/, @auth-ui/, @checklist-ui/, @project-ui/, @routes/, @primitives/, @api/, @config/, @lib/) instead of relative paths
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)
**/*.{js,jsx,ts,tsx}: Use theuseProjecthook 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
**/*.{js,jsx,ts,tsx}: For UI icons, usesolid-iconslibrary or SVGs only (never emojis)
Prefer modern ES6+ syntax and features
Use import aliases from jsconfig.json (see ui-components.mdc)
Group related components in subdirectories with barrel exports
Use Ark UI components from@corates/uipackage, NOT local components
Usesolid-iconsicon library (e.g.,solid-icons/bi,solid-icons/fi) for icons
Comments should explain WHY something is being done, not narrate what the code does
Use comments to explain why a particular approach or workaround was chosen
Use comments to clarify intent when code could be misread or misunderstood
Use comments to provide context from external systems, specs, or requirements
Use comments to document assumptions, edge cases, or limitations
Do NOT narrate what the code is doing in comments
Do NOT duplicate function or variable names in plain English in comments
Do NOT leave stale comments that contradict the code
Do NOT reference removed or obsolete code paths in comments
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/ui/src/components/index.tspackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
**/*
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*: NEVER use emojis anywhere - not in code, comments, documentation, plan files, commit messages, or examples
Do NOT use unicode symbols - unicode symbols are forbidden anywhere in the codebase
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/ui/src/components/index.tspackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/workers/src/routes/billing/index.jspackages/workers/migrations/meta/0000_snapshot.jsonpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsxpackages/workers/migrations/meta/_journal.json
**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx,jsx,js}: For UI icons, usesolid-iconslibrary or SVGs only (never emojis)
Prefer modern ES6+ syntax and features
Use import aliases from jsconfig.json as defined in the ui-components.mdc documentation
Prefer config files over hardcoding values
Ensure browser compatibility (Safari is usually problematic)
Group related components in subdirectories with barrel exports
Use Ark UI components from@corates/uipackage, not local component implementations
Usesolid-iconsicon library (e.g.,solid-icons/bi,solid-icons/fi) for icons
Comments should explain WHY something is being done, not WHAT the code is doing
Comment to explain why a particular approach or workaround was chosen
Comment to clarify intent when code could be misread or misunderstood
Comment to provide context from external systems, specs, or requirements
Comment to document assumptions, edge cases, or limitations
Do NOT comment by narrating what the code is doing
Do NOT duplicate function or variable names in plain English comments
Do NOT leave stale comments that contradict the code
Do NOT reference removed or obsolete code paths in comments
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/ui/src/components/index.tspackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
**/*.{js,jsx,ts,tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
Ensure browser compatibility (Safari is usually problematic)
Files:
packages/web/src/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/ui/src/components/index.tspackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.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/primitives/useMembers.jspackages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/lib/queryKeys.jspackages/web/src/components/billing/UsageCard.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/primitives/useSubscription.jspackages/web/src/components/billing/InvoicesList.jsx
packages/web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)
Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/components/billing/InvoicesList.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
Files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/components/billing/InvoicesList.jsx
packages/web/**/*.{ts,tsx,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/**/*.{ts,tsx,jsx}: Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure props in SolidJS - accessprops.fielddirectly or wrap in function:() => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Move business logic to stores, utilities, or primitives (not in components) in SolidJS
Files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/components/billing/InvoicesList.jsx
packages/web/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
packages/web/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure props in SolidJS - accessprops.fielddirectly or wrap in function:() => props.field
Shared state lives in external stores underpackages/web/src/stores/
SolidJS components should receive at most 1-5 props (local config only, not shared state)
UsecreateStorefor complex state objects in SolidJS
UsecreateMemofor derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/web/src/components/billing/BillingPlansPage.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/SubscriptionCard.jsxpackages/web/src/components/billing/InvoicesList.jsx
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
packages/workers/**/!(*.test|*.spec).{js,ts,jsx,tsx}: Always usecreateDomainErrorfrom@corates/sharedfor backend error handling in workers, with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, ...)with operation metadata
Use validation middleware withvalidateRequest(schema)for request validation; do not manually validate in routes
Files:
packages/workers/src/routes/billing/index.js
packages/workers/src/routes/**/*.js
📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)
packages/workers/src/routes/**/*.js: ALWAYS usevalidateRequestmiddleware for request body validation in API routes
UsevalidateQueryParamsmiddleware for query string validation in API routes
Always create DB client from environment usingcreateDbfunction in route handlers
Usedb.batch()for related database operations that must be atomic
Always use Drizzle ORM with query builders - never use raw SQL in database operations
ALWAYS usecreateDomainErrorfrom@corates/sharedfor error handling in API routes
Use error constants from@corates/shared(PROJECT_ERRORS.*,AUTH_ERRORS.*,VALIDATION_ERRORS.*,SYSTEM_ERRORS.*,USER_ERRORS.*) instead of creating error objects manually
Apply authentication and authorization middleware in correct order: Authentication → Organization membership → Project access → Authorization → Validation → Route handler
Files:
packages/workers/src/routes/billing/index.js
packages/workers/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
packages/workers/**/*.{js,ts}: Use Zod for schema and input validation on the backend
Use Drizzle ORM for ALL database interactions and migrations
Use Better-Auth for authentication and user management
All project routes are org-scoped - use/api/orgs/:orgId/projects/...pattern on backend
Backend uses orgId (UUID) for all API operations
UserequireOrgMembershipandrequireProjectAccessmiddleware for auth
Files:
packages/workers/src/routes/billing/index.js
packages/workers/src/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)
ALWAYS use
db.batch()for multiple related database operations in Drizzle to ensure atomicity - all operations must succeed or all fail together
Files:
packages/workers/src/routes/billing/index.js
packages/workers/src/routes/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)
packages/workers/src/routes/**/*.{js,ts,jsx,tsx}: ALWAYS validate request bodies usingvalidateRequestmiddleware from the validation config rather than manual validation
UsevalidateQueryParamsmiddleware for validating query parameters in routes
Files:
packages/workers/src/routes/billing/index.js
🧠 Learnings (34)
📚 Learning: 2026-01-01T23:32:23.479Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.479Z
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/primitives/useMembers.jspackages/web/src/lib/queryKeys.jspackages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:32:06.083Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-01T23:32:06.083Z
Learning: Applies to packages/workers/**/*.{js,ts} : Use `requireOrgMembership` and `requireProjectAccess` middleware for auth
Applied to files:
packages/web/src/primitives/useMembers.jspackages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-27T15:42:01.079Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T15:42:01.079Z
Learning: Applies to packages/web/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS
Applied to files:
packages/web/src/primitives/useMembers.js
📚 Learning: 2026-01-01T23:32:17.689Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/organizations.mdc:0-0
Timestamp: 2026-01-01T23:32:17.689Z
Learning: Applies to packages/web/src/primitives/useOrg* : Use useOrgProjectContext hook to combine org and project context, providing basePath and path builder functions (getStudyPath, getChecklistPath)
Applied to files:
packages/web/src/primitives/useMembers.js
📚 Learning: 2026-01-01T23:32:17.689Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/organizations.mdc:0-0
Timestamp: 2026-01-01T23:32:17.689Z
Learning: Applies to packages/web/src/components/org/** : Use useOrgContext hook to resolve org from URL orgSlug and access org state (currentOrg, orgId, isLoading, orgNotFound, hasNoOrgs)
Applied to files:
packages/web/src/primitives/useMembers.js
📚 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} : Prefer derived state with createMemo or signals over effects whenever possible
Applied to files:
packages/web/src/components/auth/ProtectedGuard.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/auth/ProtectedGuard.jsxpackages/web/src/components/billing/UsageCard.jsxpackages/ui/src/components/Spinner.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 local createSignal or createStore for local component state; use external stores for shared/cross-feature state
Applied to files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/ui/src/components/Spinner.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 Show component for conditional rendering instead of ternary operators
Applied to files:
packages/web/src/components/auth/ProtectedGuard.jsxpackages/ui/src/components/Spinner.tsxpackages/web/src/components/billing/SubscriptionCard.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/auth/ProtectedGuard.jsx
📚 Learning: 2026-01-01T23:32:06.083Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-01T23:32:06.083Z
Learning: Applies to packages/web/**/*.{jsx,tsx} : Use `createStore` for complex state objects in SolidJS
Applied to files:
packages/web/src/components/auth/ProtectedGuard.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/auth/ProtectedGuard.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/auth/ProtectedGuard.jsx
📚 Learning: 2025-12-27T15:42:01.079Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T15:42:01.079Z
Learning: Applies to packages/web/**/*.{ts,tsx} : Use `createStore` for complex state objects in SolidJS
Applied to files:
packages/web/src/components/auth/ProtectedGuard.jsx
📚 Learning: 2026-01-01T23:32:06.083Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-01T23:32:06.083Z
Learning: Applies to packages/web/**/*.{jsx,tsx} : Use `createMemo` for derived values in SolidJS
Applied to files:
packages/web/src/components/billing/UsageCard.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/ui/src/components/index.tspackages/ui/src/components/Spinner.tsx
📚 Learning: 2025-12-27T15:42:01.079Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T15:42:01.079Z
Learning: Applies to **/*.{ts,tsx,jsx,js} : Comment to provide context from external systems, specs, or requirements
Applied to files:
packages/ui/src/components/Spinner.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 the children helper when manipulating props.children in SolidJS components
Applied to files:
packages/ui/src/components/Spinner.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/ui/src/components/Spinner.tsx
📚 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/checklist/embedpdf/EmbedPdfViewerSnippet.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/checklist/embedpdf/EmbedPdfViewerSnippet.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/checklist/embedpdf/EmbedPdfViewerSnippet.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/checklist/embedpdf/EmbedPdfViewerSnippet.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/embedpdf/EmbedPdfViewerSnippet.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 `pdfPreviewStore` from `@/stores/pdfPreviewStore.js` for managing PDF preview state (openPreview, closePreview, getPreview methods)
Applied to files:
packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.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/checklist/embedpdf/EmbedPdfViewerSnippet.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 PDF operations from `useProject` hook instead of bypassing through direct API calls
Applied to files:
packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
📚 Learning: 2026-01-01T23:31:43.748Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.748Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.js : Use organization-scoped route patterns with path structure `/api/orgs/:orgId/...` and leverage `requireOrgMembership`, `requireProjectAccess`, `getOrgContext`, and `getProjectContext` middleware
Applied to files:
packages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:32:23.479Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.479Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.{js,ts,jsx,tsx} : Use `getOrgContext(c)` to retrieve the validated orgId from context instead of manual checks - orgId is guaranteed valid by middleware
Applied to files:
packages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:32:17.689Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/organizations.mdc:0-0
Timestamp: 2026-01-01T23:32:17.689Z
Learning: Applies to packages/workers/src/routes/orgs/** : Import and use requireOrgMembership middleware to verify org membership and optionally enforce minimum role requirements
Applied to files:
packages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:32:06.083Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-01T23:32:06.083Z
Learning: Applies to packages/workers/**/*.{js,ts} : All project routes are org-scoped - use `/api/orgs/:orgId/projects/...` pattern on backend
Applied to files:
packages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:31:43.748Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.748Z
Learning: Applies to packages/workers/src/routes/**/*.js : Apply authentication and authorization middleware in correct order: Authentication → Organization membership → Project access → Authorization → Validation → Route handler
Applied to files:
packages/workers/src/routes/billing/index.js
📚 Learning: 2026-01-01T23:32:06.083Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-01T23:32:06.083Z
Learning: Applies to packages/workers/**/*.sql : Do NOT create separate migration files manually (0002_xxx.sql, etc.) - use DrizzleKit instead
Applied to files:
packages/workers/migrations/meta/0000_snapshot.json
📚 Learning: 2026-01-01T23:32:17.689Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/organizations.mdc:0-0
Timestamp: 2026-01-01T23:32:17.689Z
Learning: Projects belong to organizations via orgId foreign key; org membership is separate from project membership
Applied to files:
packages/workers/migrations/meta/0000_snapshot.json
🧬 Code graph analysis (7)
packages/web/src/primitives/useMembers.js (4)
packages/web/src/components/auth/ProtectedGuard.jsx (1)
useBetterAuth(11-11)packages/web/src/primitives/useSubscription.js (2)
useBetterAuth(52-52)query(55-65)packages/web/src/lib/queryKeys.js (2)
queryKeys(8-57)queryKeys(8-57)packages/web/src/api/billing.js (1)
getMembers(112-122)
packages/web/src/components/auth/ProtectedGuard.jsx (2)
packages/ui/src/components/Spinner.tsx (1)
PageLoader(104-142)packages/ui/src/components/index.ts (1)
PageLoader(35-35)
packages/web/src/components/billing/BillingPlansPage.jsx (3)
packages/web/src/components/billing/BillingPage.jsx (1)
useSubscription(96-96)packages/web/src/primitives/useSubscription.js (2)
useSubscription(51-178)tier(80-80)packages/web/src/components/billing/PricingTable.jsx (1)
PricingTable(20-289)
packages/web/src/api/billing.js (3)
packages/web/src/primitives/useSubscription.js (1)
response(34-43)packages/web/src/api/google-drive.js (5)
response(15-20)response(30-35)response(51-60)response(70-75)response(86-98)packages/web/src/lib/error-utils.js (1)
handleFetchError(50-84)
packages/workers/src/routes/billing/index.js (5)
packages/workers/src/middleware/auth.js (5)
getAuth(63-68)session(16-18)session(38-40)auth(15-15)auth(37-37)packages/workers/src/routes/admin/orgs.js (5)
db(31-31)db(163-163)orgId(162-162)memberCountResult(189-193)memberCount(194-194)packages/workers/src/db/client.js (1)
createDb(9-11)packages/workers/src/routes/orgs/index.js (24)
orgId(93-93)orgId(138-138)orgId(179-179)orgId(206-206)orgId(237-237)orgId(288-288)orgId(342-342)orgId(403-403)auth(28-28)auth(59-59)auth(96-96)auth(143-143)auth(182-182)auth(209-209)auth(250-250)auth(302-302)auth(358-358)auth(406-406)result(29-31)result(60-68)result(97-102)result(144-155)result(210-215)result(251-258)packages/workers/src/db/schema.js (6)
session(68-84)session(68-84)member(39-49)member(39-49)user(5-26)user(5-26)
packages/web/src/components/billing/BillingPage.jsx (5)
packages/web/src/primitives/useSubscription.js (3)
useSubscription(51-178)subscription(69-69)quotas(108-108)packages/web/src/components/billing/SubscriptionCard.jsx (3)
useMembers(88-88)subscription(78-78)SubscriptionCard(77-228)packages/web/src/primitives/useMembers.js (2)
useMembers(15-38)memberCount(29-29)packages/web/src/components/billing/InvoicesList.jsx (1)
InvoicesList(124-208)packages/web/src/components/billing/UsageCard.jsx (1)
UsageCard(64-122)
packages/web/src/components/billing/SubscriptionCard.jsx (3)
packages/web/src/primitives/useSubscription.js (3)
subscription(69-69)willCancel(93-93)periodEndDate(131-142)packages/web/src/components/billing/BillingPage.jsx (1)
useMembers(97-97)packages/web/src/primitives/useMembers.js (3)
useMembers(15-38)memberCount(29-29)members(28-28)
⏰ 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). (2)
- GitHub Check: Workers Builds: corates
- GitHub Check: Workers Builds: corates-workers-prod
| return ( | ||
| <div | ||
| role='status' | ||
| aria-label={local.label ?? 'Loading'} | ||
| class={cn( | ||
| 'inline-block animate-spin rounded-full border-transparent', | ||
| sizeClasses[size()], | ||
| borderWidthClasses[size()], | ||
| variantClasses[variant()], | ||
| 'border-t-blue-200', | ||
| local.class, | ||
| )} | ||
| > | ||
| <span class='sr-only'>{local.label ?? 'Loading'}</span> | ||
| </div> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Hardcoded border-t-blue-200 breaks non-blue variants.
The border-t-blue-200 on line 74 is applied regardless of the selected variant. For variants like error, success, or warning, this creates a visual mismatch (e.g., red track with blue spinner segment).
Proposed fix - add variant-specific track colors
+// Track color mappings (lighter version of variant)
+const trackClasses = {
+ default: 'border-t-blue-200',
+ primary: 'border-t-blue-200',
+ secondary: 'border-t-gray-200',
+ success: 'border-t-green-200',
+ warning: 'border-t-yellow-200',
+ error: 'border-t-red-200',
+ white: 'border-t-white/30',
+} as const;
...
return (
<div
role='status'
aria-label={local.label ?? 'Loading'}
class={cn(
'inline-block animate-spin rounded-full border-transparent',
sizeClasses[size()],
borderWidthClasses[size()],
variantClasses[variant()],
- 'border-t-blue-200',
+ trackClasses[variant()],
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.
| return ( | |
| <div | |
| role='status' | |
| aria-label={local.label ?? 'Loading'} | |
| class={cn( | |
| 'inline-block animate-spin rounded-full border-transparent', | |
| sizeClasses[size()], | |
| borderWidthClasses[size()], | |
| variantClasses[variant()], | |
| 'border-t-blue-200', | |
| local.class, | |
| )} | |
| > | |
| <span class='sr-only'>{local.label ?? 'Loading'}</span> | |
| </div> | |
| ); | |
| }; | |
| // Track color mappings (lighter version of variant) | |
| const trackClasses = { | |
| default: 'border-t-blue-200', | |
| primary: 'border-t-blue-200', | |
| secondary: 'border-t-gray-200', | |
| success: 'border-t-green-200', | |
| warning: 'border-t-yellow-200', | |
| error: 'border-t-red-200', | |
| white: 'border-t-white/30', | |
| } as const; | |
| return ( | |
| <div | |
| role='status' | |
| aria-label={local.label ?? 'Loading'} | |
| class={cn( | |
| 'inline-block animate-spin rounded-full border-transparent', | |
| sizeClasses[size()], | |
| borderWidthClasses[size()], | |
| variantClasses[variant()], | |
| trackClasses[variant()], | |
| local.class, | |
| )} | |
| > | |
| <span class='sr-only'>{local.label ?? 'Loading'}</span> | |
| </div> | |
| ); |
🤖 Prompt for AI Agents
In @packages/ui/src/components/Spinner.tsx around lines 65-81, The Spinner
component currently hardcodes 'border-t-blue-200' which forces a blue spinner
segment regardless of the selected variant; update the component to use a
variant-specific track color instead (e.g., create a trackColorClasses mapping
or extend variantClasses to include the proper 'border-t-...' class) and replace
the hardcoded border-t-blue-200 with trackColorClasses[variant()] (or
equivalent) so the spinner's top border color matches the chosen variant while
keeping existing sizeClasses, borderWidthClasses, and local.class intact.
| return ( | ||
| <Show | ||
| when={local.overlay} | ||
| fallback={<div class='flex min-h-50 w-full items-center justify-center'>{content}</div>} | ||
| > | ||
| <div class='fixed inset-0 z-50 flex items-center justify-center bg-gray-900/20 backdrop-blur-sm'> | ||
| {content} | ||
| </div> | ||
| </Show> | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Tailwind CSS min-h min-height utility classes valid values
💡 Result:
Valid Tailwind min-height utilities (summary):
- Fixed spacing-based: min-h-0, min-h-1, min-h-2, … (all spacing scale keys like min-h-4, min-h-8, min-h-24, etc.). [1][2]
- Pixel: min-h-px (1px). [1]
- Percentage/fraction: min-h-full, min-h-1/2, min-h-2/3, min-h-3/4, min-h-9/10, etc. [1]
- Viewport: min-h-screen (100vh) and newer viewport units variants like min-h-dvh / -dvw / -lvh / -lvw / -svw (100* respective unit). [1]
- Content sizing keywords: min-h-min, min-h-max, min-h-fit (min-content / max-content / fit-content). [5][1]
- Arbitrary values: min-h-[] (e.g., min-h-[220px]) and min-h-(--var) for CSS variables. [2][1]
- You can also customize or extend the minHeight scale via theme.minHeight in tailwind.config.js. [2][1]
Sources: Tailwind docs (Sizing / min-height). [1][2]
References:
[1] Tailwind CSS docs — min-height (v4).
[2] Tailwind CSS docs — min-height (v3; arbitrary values + examples).
Replace min-h-50 with a valid Tailwind min-height utility.
min-h-50 is not a valid Tailwind spacing value. The spacing scale uses discrete values like min-h-48 (192px) and min-h-52 (208px). Use either the closest standard value or arbitrary syntax: min-h-[200px].
🤖 Prompt for AI Agents
In @packages/ui/src/components/Spinner.tsx around lines 132-141, The fallback
div in the Spinner component uses an invalid Tailwind class `min-h-50`; update
that class to a valid utility (e.g., `min-h-48` or `min-h-52`) or use arbitrary
value syntax like `min-h-[200px]` in the JSX returned by the Show fallback to
ensure the min-height is applied correctly (locate the fallback div inside the
Show in Spinner.tsx where `fallback={<div class='flex min-h-50 w-full
items-center justify-center'>{content}</div>}` is rendered).
| Need help with billing?{' '} | ||
| <a href='#' class='font-medium text-blue-600 hover:text-blue-700'> | ||
| Contact support | ||
| </a>{' '} | ||
| and we'll get back to you within 24 hours. | ||
| </p> |
There was a problem hiding this comment.
Placeholder href='#' creates a non-functional link.
The "Contact support" link uses href='#' which scrolls to top but doesn't navigate anywhere. Use the same LANDING_URL pattern as the QuickAction above or remove until implemented.
Proposed fix
Need help with billing?{' '}
- <a href='#' class='font-medium text-blue-600 hover:text-blue-700'>
+ <a
+ href={`${LANDING_URL}/contact`}
+ target='_blank'
+ rel='noopener noreferrer'
+ class='font-medium text-blue-600 hover:text-blue-700'
+ >
Contact support
</a>{' '}🤖 Prompt for AI Agents
In @packages/web/src/components/billing/BillingPage.jsx around lines 217-222,
The Contact support anchor in BillingPage.jsx currently uses a placeholder href
('#') which is non-functional; update the anchor in the BillingPage component to
use the same LANDING_URL pattern used by the QuickAction above (replace href='#'
with the LANDING_URL constant or computed link) or remove the anchor until a
real destination exists; locate the anchor element rendering "Contact support"
and swap its href to LANDING_URL (or delete the <a> and render plain text) and
ensure you import or reference the existing LANDING_URL symbol consistently with
QuickAction.
| </div> | ||
|
|
||
| {/* Bottom CTA */} | ||
| <div class='mt-16 rounded-2xl bg-linear-to-r from-blue-600 to-blue-500 px-8 py-12 text-center'> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/web/src/components/billing/BillingPlansPage.jsx | sed -n '150,162p'Repository: InfinityBowman/corates
Length of output: 718
🌐 Web query:
Tailwind CSS gradient class syntax bg-gradient-to-r documentation
💡 Result:
Syntax summary — use these classes together:
- Direction: bg-gradient-to-r (sets background-image: linear-gradient(to right, var(--tw-gradient-stops))). [1][2]
- Color stops: from-{color}, via-{color} (optional), to-{color}. Example: class="bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500". [1]
- Stop positions: from-10%, via-30%, to-90% to control stop locations. [1]
- Advanced: interpolation modifiers (e.g., bg-linear-to-r/srgb) and custom values (bg-linear-[...], from-[#hex]) are supported. [1]
Minimal example:
Sources: Tailwind CSS docs and class reference. [1][2]
Replace invalid Tailwind class bg-linear-to-r with bg-gradient-to-r.
Tailwind CSS uses bg-gradient-to-r for gradient backgrounds, not bg-linear-to-r.
Fix
- <div class='mt-16 rounded-2xl bg-linear-to-r from-blue-600 to-blue-500 px-8 py-12 text-center'>
+ <div class='mt-16 rounded-2xl bg-gradient-to-r from-blue-600 to-blue-500 px-8 py-12 text-center'>📝 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.
| <div class='mt-16 rounded-2xl bg-linear-to-r from-blue-600 to-blue-500 px-8 py-12 text-center'> | |
| <div class='mt-16 rounded-2xl bg-gradient-to-r from-blue-600 to-blue-500 px-8 py-12 text-center'> |
🤖 Prompt for AI Agents
In @packages/web/src/components/billing/BillingPlansPage.jsx around line 156, In
BillingPlansPage.jsx update the div that currently uses the invalid Tailwind
class 'bg-linear-to-r' (the div with class='mt-16 rounded-2xl bg-linear-to-r
from-blue-600 to-blue-500 px-8 py-12 text-center') to use the correct Tailwind
gradient utility 'bg-gradient-to-r' so the gradient styles (from-blue-600
to-blue-500) apply correctly.
| <Show when={invoices()?.invoices?.length > 0}> | ||
| <button | ||
| type='button' | ||
| class='text-sm font-medium text-blue-600 transition-colors hover:text-blue-700' | ||
| > | ||
| View all | ||
| </button> | ||
| </Show> |
There was a problem hiding this comment.
"View all" button has no onClick handler.
The button is rendered but doesn't navigate or perform any action. Either add functionality or remove the button until the feature is implemented.
Proposed fix - remove until implemented
- <Show when={invoices()?.invoices?.length > 0}>
- <button
- type='button'
- class='text-sm font-medium text-blue-600 transition-colors hover:text-blue-700'
- >
- View all
- </button>
- </Show>📝 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.
| <Show when={invoices()?.invoices?.length > 0}> | |
| <button | |
| type='button' | |
| class='text-sm font-medium text-blue-600 transition-colors hover:text-blue-700' | |
| > | |
| View all | |
| </button> | |
| </Show> |
🤖 Prompt for AI Agents
In @packages/web/src/components/billing/InvoicesList.jsx around lines 144-151,
The "View all" button inside the Show wrapper renders with no onClick and does
nothing; remove the button block (the <button> element inside the Show that
checks invoices()?.invoices?.length) until the feature is implemented, or
alternatively add an onClick that performs a real action (e.g., call a
navigation function like navigate('/billing/invoices') or toggle a state handler
such as setShowAll(true)); update any references to the Show wrapper accordingly
so there are no unused UI fragments.
| <Show when={isUnlimited()}> | ||
| <div class='h-2 w-full rounded-full bg-linear-to-r from-green-100 to-green-200' /> | ||
| </Show> |
There was a problem hiding this comment.
Invalid Tailwind class: bg-linear-to-r should be bg-gradient-to-r.
Tailwind CSS uses bg-gradient-to-r for linear gradients, not bg-linear-to-r. This will render as a missing style.
Proposed fix
<Show when={isUnlimited()}>
- <div class='h-2 w-full rounded-full bg-linear-to-r from-green-100 to-green-200' />
+ <div class='h-2 w-full rounded-full bg-gradient-to-r from-green-100 to-green-200' />
</Show>📝 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.
| <Show when={isUnlimited()}> | |
| <div class='h-2 w-full rounded-full bg-linear-to-r from-green-100 to-green-200' /> | |
| </Show> | |
| <Show when={isUnlimited()}> | |
| <div class='h-2 w-full rounded-full bg-gradient-to-r from-green-100 to-green-200' /> | |
| </Show> |
🤖 Prompt for AI Agents
In @packages/web/src/components/billing/UsageCard.jsx around lines 51-53, The
Tailwind class used for the gradient is incorrect: replace the `bg-linear-to-r`
class on the div inside the Show block (the element rendered when isUnlimited()
is true) with `bg-gradient-to-r`; keep the rest of the class string (`h-2 w-full
rounded-full from-green-100 to-green-200`) unchanged so the gradient renders
correctly.
| // Return default subscription when not logged in or when query fails | ||
| // This prevents breaking the UI while still exposing the error for components that care | ||
| const subscription = () => query.data || DEFAULT_SUBSCRIPTION; | ||
| console.log(subscription()); |
There was a problem hiding this comment.
Remove debug console.log before merging.
This debug statement will execute on every component render that uses useSubscription, polluting the console and potentially exposing subscription details (tier, status, Stripe IDs) in production.
Proposed fix
const subscription = () => query.data || DEFAULT_SUBSCRIPTION;
- console.log(subscription());📝 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.
| console.log(subscription()); | |
| const subscription = () => query.data || DEFAULT_SUBSCRIPTION; |
🤖 Prompt for AI Agents
In @packages/web/src/primitives/useSubscription.js around line 70, Remove the
debug console.log in useSubscription: delete the line calling
console.log(subscription()) inside the useSubscription hook so subscription
details are not printed on every render; search for the useSubscription function
and ensure no other transient debug console statements remain (do not replace
with logging of sensitive data).
| // Get member count for convenience | ||
| const memberCountResult = await db | ||
| .select({ count: count() }) | ||
| .from(member) | ||
| .where(eq(member.organizationId, orgId)) | ||
| .get(); | ||
| const memberCount = memberCountResult?.count || 0; | ||
|
|
||
| return c.json({ | ||
| members: result.members || [], | ||
| count: memberCount, | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the full context of the billing route file
cat -n packages/workers/src/routes/billing/index.js | head -160Repository: InfinityBowman/corates
Length of output: 6555
🏁 Script executed:
# Also check for other member count patterns in the codebase
rg -n -A3 "select.*count\(\)" --type js packages/workers/src/Repository: InfinityBowman/corates
Length of output: 5398
🏁 Script executed:
# Search for other uses of listMembers to understand the response structure
rg -n "listMembers" --type js packages/workers/src/Repository: InfinityBowman/corates
Length of output: 361
🏁 Script executed:
# Check if there's any documentation about the Better-Auth listMembers API
rg -n "listMembers" --type ts --type js packages/ --max-count 20Repository: InfinityBowman/corates
Length of output: 361
🏁 Script executed:
# Check how orgs/index.js handles the result from listMembers
sed -n '210,230p' packages/workers/src/routes/orgs/index.jsRepository: InfinityBowman/corates
Length of output: 614
🏁 Script executed:
# Look at the test to understand what mockListMembers returns
sed -n '70,85p' packages/workers/src/routes/__tests__/orgs-management.test.jsRepository: InfinityBowman/corates
Length of output: 651
🏁 Script executed:
# Find the mockListMembers implementation in the test file
rg -n "mockListMembers" -A 10 packages/workers/src/routes/__tests__/orgs-management.test.js | head -30Repository: InfinityBowman/corates
Length of output: 962
🏁 Script executed:
# Also search for any Better-Auth documentation or config in the auth folder
find packages/workers/src/auth -type f -name "*.js" -o -name "*.ts" | head -10Repository: InfinityBowman/corates
Length of output: 290
🏁 Script executed:
# Find the test case for listing members to see the expected response structure
rg -n "should list members when user is org member" -A 20 packages/workers/src/routes/__tests__/orgs-management.test.jsRepository: InfinityBowman/corates
Length of output: 624
🏁 Script executed:
# Check the Better-Auth config to understand how listMembers is implemented
sed -n '1,50p' packages/workers/src/auth/config.jsRepository: InfinityBowman/corates
Length of output: 1949
🏁 Script executed:
# Check the full test case to see what the response structure should look like
rg -n "should list members when user is org member" -A 50 packages/workers/src/routes/__tests__/orgs-management.test.js | tail -40Repository: InfinityBowman/corates
Length of output: 1136
🏁 Script executed:
# Search for how the billing/members endpoint is tested or used
rg -n "billing/members" --type js packages/workers/Repository: InfinityBowman/corates
Length of output: 142
🏁 Script executed:
# Check the orgs/index.js implementation for /api/orgs/:orgId/members to see if it does the same
sed -n '205,225p' packages/workers/src/routes/orgs/index.jsRepository: InfinityBowman/corates
Length of output: 680
Use result.members?.length instead of separate count query for consistency and efficiency.
The code fetches the full members array from Better-Auth via listMembers() but then makes a separate database count query. This is inefficient and creates a potential inconsistency—if members are added/removed between the two calls, the count won't match members.length. Since the full list is already available, use the array length directly as shown in the similar endpoint at /api/orgs/:orgId/members.
🤖 Prompt for AI Agents
In @packages/workers/src/routes/billing/index.js around lines 143-154, The code
makes a redundant DB count query (memberCountResult / memberCount) after
fetching the full members array (result.members) which is inefficient and can
lead to inconsistencies; replace the separate count query by deriving the count
from result.members (e.g., use result.members?.length or 0) and return that as
the count in the JSON response, removing the memberCountResult/memberCount
database query and references to it; keep the returned members array as
result.members || [] to match the existing pattern.
… 226-better-billing-page
…Bowman/corates into 226-better-billing-page
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.