Skip to content

226 better billing page#227

Merged
InfinityBowman merged 9 commits into
mainfrom
226-better-billing-page
Jan 5, 2026
Merged

226 better billing page#227
InfinityBowman merged 9 commits into
mainfrom
226-better-billing-page

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Jan 5, 2026

Summary by CodeRabbit

  • New Features

    • New loading spinner components with multiple styles for consistent loading states across the application.
    • Redesigned billing dashboard with subscription, usage, and invoice management sections.
    • Enhanced billing plans page with FAQ section and trust signal badges.
    • Usage quota tracking display for team member limits.
  • Improvements

    • Pricing table redesigned with annual savings calculations and visual enhancements.
    • Invoice interface updated with status indicators and improved layout.
    • Authentication loading experiences improved with new spinner components.

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

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

coderabbitai Bot commented Jan 5, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Spinner Component Suite
packages/ui/src/components/Spinner.tsx, packages/ui/src/components/index.ts
Introduces four new loading components: Spinner (configurable circular loader with size/variant), PageLoader (full-page/overlay loading), LoadingPlaceholder (space-reserving skeleton), and ButtonSpinner (button-integrated spinner). All components expose configurable size and variant props. Exports added to barrel index.
Protected Route Loading
packages/web/src/components/auth/ProtectedGuard.jsx
Refactored to remove initial loading signal and createEffect tracking; now relies solely on isLoggedIn() via Show primitive. Replaces previous loading UI with direct PageLoader component import and usage as fallback. Retains redirect logic to dashboard for non-authenticated users.
Billing API & Query Infrastructure
packages/web/src/api/billing.js, packages/workers/src/routes/billing/index.js, packages/web/src/lib/queryKeys.js, packages/web/src/primitives/useMembers.js
Adds getMembers() client function and corresponding GET /api/billing/members backend endpoint. Introduces queryKeys.members namespace and new useMembers hook that fetches members via TanStack Query with 5-minute staleTime. Hook exposes members, memberCount, loading, error, and refetch selectors.
Billing Page Redesign
packages/web/src/components/billing/BillingPage.jsx
Replaces simple layout with dashboard-style multi-section design. Integrates subscription data, member count, and computed usage metrics. New two-column layout: left contains SubscriptionCard/skeleton and InvoicesList; right contains UsageCard/skeleton, quick actions, and help CTA. Adds QuickAction, SubscriptionSkeleton, UsageSkeleton internal components.
Pricing & Plans UI
packages/web/src/components/billing/PricingTable.jsx, packages/web/src/components/billing/BillingPlansPage.jsx
PricingTable redesigned with premium visuals, hover states, annual billing toggle, savings badge, and richer card variants (current/popular/default). Adds getAnnualSavings() helper and improved feature list display. BillingPlansPage adds header redesign, introduction text, FAQ section with FAQItem accordion component, TrustBadge components for trust signals, and bottom CTA block.
Card Components
packages/web/src/components/billing/SubscriptionCard.jsx, packages/web/src/components/billing/UsageCard.jsx, packages/web/src/components/billing/InvoicesList.jsx
SubscriptionCard redesigned with gradient header, trial countdown, StatusBadge, team members list, and Switch-based manage/upgrade flow. Props changed: removed onUpgrade/loading, added manageLoading. UsageCard (new) displays quota metrics with progress bars, handles unlimited quotas, and adapts visual variant by utilization level. InvoicesList replaces simple display with PremiumInvoices UI: status badges, empty state, loading skeleton, real API fetch, and improved download/view actions.
Formatting & Schema Changes
packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx, packages/workers/migrations/meta/0000_snapshot.json, packages/workers/migrations/meta/_journal.json, packages/web/src/primitives/useSubscription.js
Minor multi-line formatting adjustments to querySelector calls and ternary expression (no behavior change). JSON migration snapshot reformatted with inlined array literals for foreignKeys and indexes. Trailing newline added to _journal.json. Console.log added to useSubscription hook after subscription getter definition.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

  • Better billing page #226: This PR directly implements the comprehensive redesign and feature expansion for "Better billing page" through BillingPage, PricingTable, SubscriptionCard, InvoicesList, UsageCard components, plus supporting hooks (useMembers) and API endpoints (/api/billing/members).

Possibly related PRs

  • Account linking #53: PR introduced ProtectedGuard.jsx; this PR modifies it to import and use the new PageLoader component, directly updating its loading state handling.
  • 212 stripe better auth #214: Both PRs modify packages/web/src/api/billing.js — this PR adds getMembers() function alongside other billing API surface changes.
  • add tanstack query #193: This PR builds on TanStack Query infrastructure by introducing queryKeys.members and useMembers hook that leverages the Query setup from that PR.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title '226 better billing page' is vague and primarily references an issue number rather than describing the actual changes made. Consider revising the title to clearly describe the main changes, such as 'Redesign billing page with subscription, usage, and invoices sections' or 'Add comprehensive billing dashboard with member management'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

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

cloudflare-workers-and-pages Bot commented Jan 5, 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 7dcd75a Commit Preview URL Jan 05 2026, 06:40 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: 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 if statement executes on every component re-render when checkoutSuccess() is true, potentially causing repeated API calls. Wrap it in createEffect to 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 using ButtonSpinner from @corates/ui for consistency.

This PR introduces a ButtonSpinner component 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 - member and eq are already imported at the top of the file.

Lines 10-11 already import member from schema and eq from 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 orgId from 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 formatAmount function 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/ui instead of inline SVG.

This PR introduces Spinner and ButtonSpinner components 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?.name is 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

📥 Commits

Reviewing files that changed from the base of the PR and between c1519d1 and e6108ce.

📒 Files selected for processing (17)
  • packages/ui/src/components/Spinner.tsx
  • packages/ui/src/components/index.ts
  • packages/web/src/api/billing.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/components/billing/InvoicesList.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/primitives/useMembers.js
  • packages/web/src/primitives/useSubscription.js
  • packages/workers/migrations/meta/0000_snapshot.json
  • packages/workers/migrations/meta/_journal.json
  • packages/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 use handleFetchError from @/lib/error-utils.js for frontend fetch calls with optional showToast parameter
Use createFormErrorSignals from @/lib/form-errors.js for handling form validation errors, field-level errors, and global errors in frontend forms

Files:

  • packages/web/src/primitives/useMembers.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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
Use isErrorCode utility from @corates/shared or @/lib/error-utils.js to check for specific error codes instead of manual error comparisons

Files:

  • packages/web/src/primitives/useMembers.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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 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/primitives/useMembers.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/ui/src/components/index.ts
  • packages/ui/src/components/Spinner.tsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/web/src/components/billing/InvoicesList.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

**/*.{js,jsx,ts,tsx}: For UI icons, use solid-icons library 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/ui package, NOT local components
Use solid-icons icon 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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/ui/src/components/index.ts
  • packages/ui/src/components/Spinner.tsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/ui/src/components/index.ts
  • packages/ui/src/components/Spinner.tsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/workers/migrations/meta/0000_snapshot.json
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/web/src/components/billing/InvoicesList.jsx
  • packages/workers/migrations/meta/_journal.json
**/*.{ts,tsx,jsx,js}

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

**/*.{ts,tsx,jsx,js}: For UI icons, use solid-icons library 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/ui package, not local component implementations
Use solid-icons icon 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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/ui/src/components/index.ts
  • packages/ui/src/components/Spinner.tsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/ui/src/components/index.ts
  • packages/ui/src/components/Spinner.tsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/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.js
  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/lib/queryKeys.js
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/web/src/primitives/useSubscription.js
  • packages/web/src/components/billing/InvoicesList.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/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/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.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/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 - 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)
Move business logic to stores, utilities, or primitives (not in components) in SolidJS

Files:

  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/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 - access props.field directly or wrap in function: () => props.field
Shared state lives in external stores under packages/web/src/stores/
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
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability

Files:

  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/web/src/components/billing/BillingPlansPage.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/checklist/embedpdf/EmbedPdfViewerSnippet.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/SubscriptionCard.jsx
  • packages/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 use createDomainError from @corates/shared for 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 using createDomainError(SYSTEM_ERRORS.DB_ERROR, ...) with operation metadata
Use validation middleware with validateRequest(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 use validateRequest middleware for request body validation in API routes
Use validateQueryParams middleware for query string validation in API routes
Always create DB client from environment using createDb function in route handlers
Use db.batch() for related database operations that must be atomic
Always use Drizzle ORM with query builders - never use raw SQL in database operations
ALWAYS use createDomainError from @corates/shared for 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
Use requireOrgMembership and requireProjectAccess middleware 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 using validateRequest middleware from the validation config rather than manual validation
Use validateQueryParams middleware 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.js
  • packages/web/src/lib/queryKeys.js
  • 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} : Use `requireOrgMembership` and `requireProjectAccess` middleware for auth

Applied to files:

  • packages/web/src/primitives/useMembers.js
  • packages/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.jsx
  • packages/web/src/components/billing/UsageCard.jsx
  • 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/**}/**/*.{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.jsx
  • 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 Show component for conditional rendering instead of ternary operators

Applied to files:

  • packages/web/src/components/auth/ProtectedGuard.jsx
  • packages/ui/src/components/Spinner.tsx
  • packages/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.ts
  • packages/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

Comment on lines +65 to +81
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>
);
};
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

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.

Suggested change
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.

Comment on lines +132 to +141
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>
);
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

🧩 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).

Comment on lines +217 to +222
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>
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

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'>
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

🧩 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.

Suggested change
<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.

Comment on lines +144 to +151
<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>
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

"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.

Suggested change
<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.

Comment on lines +51 to +53
<Show when={isUnlimited()}>
<div class='h-2 w-full rounded-full bg-linear-to-r from-green-100 to-green-200' />
</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

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.

Suggested change
<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());
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

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.

Suggested change
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).

Comment on lines +143 to +154
// 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,
});
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

🧩 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 -160

Repository: 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 20

Repository: 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.js

Repository: 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.js

Repository: 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 -30

Repository: 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 -10

Repository: 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.js

Repository: 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.js

Repository: 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 -40

Repository: 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.js

Repository: 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.

@InfinityBowman InfinityBowman merged commit 727fa91 into main Jan 5, 2026
2 of 4 checks passed
@InfinityBowman InfinityBowman deleted the 226-better-billing-page branch January 5, 2026 18:38
@github-actions github-actions Bot restored the 226-better-billing-page branch January 5, 2026 18:38
@InfinityBowman InfinityBowman deleted the 226-better-billing-page branch January 5, 2026 18:39
@coderabbitai coderabbitai Bot mentioned this pull request Apr 18, 2026
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.

Better billing page

2 participants