-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Bulk Archive #1198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Bulk Archive #1198
Conversation
|
@jshwrnr is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds a bulk-email archival feature: redirect routes, account-scoped pages, client UIs for selecting/archiving senders and progress, server APIs and batch handling via a pluggable email provider, classifier utilities + tests, and category/AI-constant renames. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Browser as Client (Browser)
participant Server as App Server / Page
participant DB as Database (Prisma)
participant Provider as Email Provider
participant Queue as Archive Queue
User->>Browser: Open /bulk-archive (redirect)
Browser->>Server: Request account-scoped BulkArchive page
Server->>DB: validate ownership & fetch senders + categories
DB-->>Server: senders + categories
Server-->>Browser: render BulkArchive UI with initial data
User->>Browser: Enable auto-categorize
Browser->>Server: POST bulkCategorizeSendersAction
Server->>DB: ensure default categories & set autoCategorizeSenders
DB-->>Server: success
Server-->>Browser: start progress polling
User->>Browser: Select senders → Archive
Browser->>Queue: enqueue archive jobs
loop per job
Queue->>Provider: fetch threads for sender
Provider-->>Queue: return threads
Queue->>DB: update archive state
DB-->>Browser: progress updates (via API/SWR)
Browser->>Browser: update UI
end
Queue-->>Browser: completion → show final state
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Fix all issues with AI Agents 🤖
In @apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx:
- Around line 125-130: The BulkArchiveTab component's props include an unused
categories parameter; remove categories from the component signature and any
related type annotation so the props only accept emailGroups (adjust the
exported function signature BulkArchiveTab({ emailGroups }: { emailGroups:
EmailGroup[] }) and any callers/usage if they pass categories), or
alternatively, if categories are needed, use the categories variable inside
BulkArchiveTab where appropriate; pick one approach and update the function
signature and type annotations accordingly to eliminate the unused prop warning.
🧹 Nitpick comments (7)
apps/web/app/(app)/early-access/page.tsx (1)
24-24: Remove unnecessary fragment wrapper.Lines 24 and 68 wrap the Sender Categories card in a fragment (
<></>) that only contains a single conditional Card. Fragments are only needed when you have multiple sibling elements. If these commented-out card blocks are removed (per the previous comment), the fragment becomes unnecessary and should be removed.🔎 Proposed fix
{isGoogleProvider(provider) && ( - <> - <Card> + <Card> <CardHeader> <CardTitle>Sender Categories</CardTitle> <CardDescription>And at the end:
- {/* ... commented cards ... */} - </> + {/* ... commented cards removed ... */}Also applies to: 68-68
apps/web/components/BulkArchiveCards.tsx (2)
63-118: Move helper function to the bottom of the file.Per coding guidelines, helper functions should be placed at the bottom of files, not the top. Consider moving
getCategoryIconafter theExpandedEmailscomponent.
221-236: Consider parallelizing archive queue additions.Using
awaitinside aforloop processes senders sequentially, which may be slower than necessary. Per coding guidelines, avoidawaitinside loops when parallel execution is safe.🔎 Proposed fix using Promise.all
const archiveCategory = async (categoryName: string, e: React.MouseEvent) => { e.stopPropagation(); const senders = groupedEmails[categoryName] || []; const selectedToArchive = senders.filter( (s) => selectedSenders[s.address] !== false, ); - for (const sender of selectedToArchive) { - await addToArchiveSenderQueue({ + await Promise.all( + selectedToArchive.map((sender) => + addToArchiveSenderQueue({ sender: sender.address, emailAccountId, - }); - } + }), + ), + ); setArchivedCategories((prev) => ({ ...prev, [categoryName]: true })); };apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (1)
23-33: Consider extracting shared data-fetching logic.This data-fetching pattern (senders with categories + user categories with rules) is duplicated between
bulk-archive/page.tsxandquick-bulk-archive/page.tsx. Consider extracting to a shared utility function.// Example: apps/web/utils/bulk-archive.server.ts export async function getBulkArchiveData(emailAccountId: string) { const [senders, categories] = await Promise.all([ prisma.newsletter.findMany({ where: { emailAccountId, categoryId: { not: null } }, select: { id: true, email: true, category: { select: { id: true, description: true, name: true } }, }, }), getUserCategoriesWithRules({ emailAccountId }), ]); return { senders, categories }; }apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (3)
226-239: Consider parallelizing archive queue additions.Same issue as in
BulkArchiveCards.tsx- usingawaitinside aforloop processes senders sequentially. Per coding guidelines, avoidawaitinside loops when parallel execution is safe.🔎 Proposed fix using Promise.all
const archiveSelected = async () => { setIsArchiving(true); const toArchive = candidates.filter((c) => selectedSenders[c.address]); - for (const candidate of toArchive) { - await addToArchiveSenderQueue({ + await Promise.all( + toArchive.map((candidate) => + addToArchiveSenderQueue({ sender: candidate.address, emailAccountId, - }); - } + }), + ), + ); setArchiveComplete(true); setIsArchiving(false); };
48-90: Move helper function to the bottom of the file.Per coding guidelines, helper functions should be placed at the bottom of files. Consider moving
getArchiveCandidatesafter theExpandedEmailscomponent. The TODO comment suggests this is temporary, so you may want to address placement when implementing the actual AI-derived data.
483-681: Significant code duplication with BulkArchiveCards.tsx.
SenderRow,ArchiveStatus, andExpandedEmailsare nearly identical in bothBulkArchiveTab.tsxandBulkArchiveCards.tsx. Consider extracting these to shared components in a common location (e.g.,@/components/bulk-archive/) to reduce duplication and ensure consistent behavior.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/early-access/page.tsxapps/web/app/(marketing)apps/web/components/BulkArchiveCards.tsxapps/web/components/EmailCell.tsx
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/app/(app)/*/page.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/*/page.tsx: Create new pages atapps/web/app/(app)/PAGE_NAME/page.tsx
Pages are Server components so you can load data into them directlyCreate new pages at
apps/web/app/(app)/PAGE_NAME/page.tsxwith components either colocated in the same folder or inpage.tsx
Files:
apps/web/app/(app)/early-access/page.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(marketing)apps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/components/EmailCell.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/EmailCell.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/EmailCell.tsx
🧠 Learnings (17)
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/early-access/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Early access features are automatically displayed on the Early Access page (`/early-access`) through the `EarlyAccessFeatures` component, requiring no manual configuration
Applied to files:
apps/web/app/(app)/early-access/page.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Early access features are automatically displayed on the Early Access page (`/early-access`) through the `EarlyAccessFeatures` component with no manual configuration needed
Applied to files:
apps/web/app/(app)/early-access/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/(redirects)/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/**/*.{ts,tsx} : Follow NextJS app router structure with (app) directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/*/page.tsx : Pages must be Server components that load data directly
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components so you can load data into them directly
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
🧬 Code graph analysis (4)
apps/web/app/(app)/early-access/page.tsx (2)
apps/web/components/new-landing/common/Card.tsx (3)
Card(67-105)CardHeader(22-54)CardContent(10-12)apps/web/components/new-landing/icons/Link.tsx (1)
Link(1-18)
apps/web/app/(app)/(redirects)/bulk-archive/page.tsx (2)
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (1)
BulkArchivePage(15-65)apps/web/utils/account.ts (1)
redirectToEmailAccountPath(136-160)
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx (5)
apps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsx (1)
QuickBulkArchivePage(3-5)apps/web/utils/email-account.ts (1)
checkUserOwnsEmailAccount(5-22)apps/web/utils/category.server.ts (1)
getUserCategoriesWithRules(24-39)apps/web/app/(app)/[emailAccountId]/PermissionsCheck.tsx (1)
PermissionsCheck(12-35)apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (1)
BulkArchiveTab(125-481)
apps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsx (1)
apps/web/utils/account.ts (1)
redirectToEmailAccountPath(136-160)
⏰ 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: cubic · AI code reviewer
- GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (10)
apps/web/app/(app)/early-access/page.tsx (1)
40-67: The commented-out code is intentional and requires no changes.The bulk archive features are fully implemented and routable. The early access page correctly uses PostHog's
EarlyAccessFeaturescomponent, which manages features through PostHog's dedicated early access/beta enrollment system—not through feature flags. The commented cards represent features awaiting PostHog early access configuration. Once configured in PostHog, they will be automatically displayed through theEarlyAccessFeaturescomponent. No feature flag hooks or code changes are needed at this time.apps/web/app/(marketing) (1)
1-1: Incomplete review context: actual code files not provided.The file shows only a git submodule pointer update, but the AI summary references multiple new implementation files (redirect pages, bulk-archive pages, BulkArchiveCards component, BulkArchiveTab component, etc.) that are not provided in this review context.
To perform a thorough code review, please provide the actual implementation files mentioned in the summary so I can assess:
- Correctness of page logic and data validation
- Client-side component implementation and state management
- Adherence to Next.js best practices (e.g., the coding guideline about avoiding next/head in pages/_document.js)
- Security and ownership validation in the new routes
- Error handling and edge cases
apps/web/components/EmailCell.tsx (1)
17-23: LGTM!Clean implementation that avoids redundant subtitle display when the name and email are identical (e.g., plain email addresses without a display name).
apps/web/app/(app)/(redirects)/bulk-archive/page.tsx (1)
1-5: LGTM!Follows the established redirect pattern and correctly uses the
redirectToEmailAccountPathutility. Based on learnings, the page structure follows project conventions.apps/web/components/BulkArchiveCards.tsx (2)
1-61: LGTM!Correct use of
"use client"directive, lodash import pattern following coding guidelines (specific function import), and properly scoped local types.
556-660: LGTM!
ExpandedEmailscorrectly uses theuseThreadshook (SWR pattern per coding guidelines) and properly handles loading, error, and empty states. The snippet sanitization logic appropriately removes invisible characters.apps/web/app/(app)/(redirects)/quick-bulk-archive/page.tsx (1)
1-5: LGTM!Follows the established redirect pattern consistently with the bulk-archive redirect page.
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx (1)
1-65: LGTM!Well-structured server component following Next.js app router patterns. Correctly uses parallel data fetching with
Promise.all, ownership validation viacheckUserOwnsEmailAccount, and proper data transformation before passing to the client component. Based on learnings, this follows the page structure conventions.apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (1)
1-65: LGTM on the implementation.Correctly follows the established server component patterns with ownership validation, parallel data fetching, and proper component composition.
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (1)
241-288: LGTM on state handling.Good handling of empty state and archive completion state with appropriate user feedback. The reset functionality after archiving is a nice UX touch.
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 issues found across 9 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx:226">
P1: Missing error handling in async function. If `addToArchiveSenderQueue` fails, the UI will be stuck in the archiving state with the button permanently disabled. Wrap in try/catch and reset state in finally block.</violation>
<violation number="2" location="apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx:627">
P2: Potential runtime error if `thread.messages` is empty. Consider adding a guard to skip threads without messages.</violation>
</file>
<file name="apps/web/components/BulkArchiveCards.tsx">
<violation number="1" location="apps/web/components/BulkArchiveCards.tsx:602">
P1: Potential crash: `thread.messages[0]` is accessed without checking if the array is empty. If `messages` is empty, this will throw a runtime error when accessing `.subject` on undefined.</violation>
<violation number="2" location="apps/web/components/BulkArchiveCards.tsx:648">
P2: Dead code: The 'more emails' section will never be shown. Since `useThreads` is called with `limit: 5`, `data.threads.length` will always be <= 5, making `totalCount > displayedCount` always false. If you want to show a 'more' indicator, you need the API to return a total count separately from the paginated results.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
Show resolved
Hide resolved
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (1)
30-63: UsetoastErrorutility for consistent error handling.The error handling implementation works but doesn't follow the established pattern. The coding guidelines specify using
toastErrorfrom@/components/Toastfor consistency across the codebase.🔎 Suggested refactor
+import { toastError, toastSuccess } from "@/components/Toast"; -import { toast } from "sonner"; try { // ... existing code ... - toast.success( + toastSuccess({ + description: categorizeResult?.data?.totalUncategorizedSenders ? `Categorizing ${categorizeResult.data.totalUncategorizedSenders} senders... This may take a few minutes.` : "No uncategorized senders found.", - ); + }); } catch (error) { - toast.error( - `Failed to enable feature: ${error instanceof Error ? error.message : "Unknown error"}`, - ); + toastError({ + description: `Failed to enable feature: ${error instanceof Error ? error.message : "Unknown error"}`, + }); setIsBulkCategorizing(false); }As per coding guidelines, use
toastErrorandtoastSuccessfrom@/components/Toastfor error handling in async operations.apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (1)
6-6: Consider moving ArchiveProgress to a shared location.The component is imported from the
bulk-unsubscribefolder while this page is inbulk-archive. IfArchiveProgressis intended to be shared across features, consider moving it to a common location like@/components/or@/app/(app)/[emailAccountId]/components/to improve discoverability and reduce cross-feature coupling.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/utils/actions/categorize.ts
🧰 Additional context used
📓 Path-based instructions (28)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
apps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/utils/actions/*.ts: Usenext-safe-actionwith Zod schemas for all server actions (create/update/delete mutations), storing validation schemas inapps/web/utils/actions/*.validation.ts
Server actions should use 'use server' directive and automatically receive authentication context (emailAccountId) from theactionClient
apps/web/utils/actions/*.ts: Create corresponding server action implementation files using the naming conventionapps/web/utils/actions/NAME.tswith 'use server' directive
Use 'use server' directive at the top of server action implementation files
Implement all server actions using thenext-safe-actionlibrary with actionClient, actionClientUser, or adminActionClient for type safety and validation
UseactionClientUserwhen only authenticated user context (userId) is needed
UseactionClientwhen both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
UseadminActionClientfor actions restricted to admin users
Add metadata with a meaningful action name using.metadata({ name: "actionName" })for Sentry instrumentation and monitoring
Use.schema()method with Zod validation schemas from corresponding.validation.tsfiles in next-safe-action configuration
Access context (userId, emailAccountId, etc.) via thectxobject parameter in the.action()handler
UserevalidatePathorrevalidateTagfrom 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Files:
apps/web/utils/actions/categorize.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/actions/**/*.ts: Server actions must be located inapps/web/utils/actionsfolder
Server action files must start withuse serverdirective
apps/web/utils/actions/**/*.ts: Use proper error handling with try/catch blocks
Usenext-safe-actionwith Zod schemas for server actions to handle mutations
UserevalidatePathin server actions for cache invalidation after mutations
Files:
apps/web/utils/actions/categorize.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/actions/categorize.ts
apps/web/utils/actions/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Infer types from Zod schemas using
z.infer<typeof schema>instead of duplicating as separate interfaces
Files:
apps/web/utils/actions/categorize.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/actions/categorize.tsapps/web/app/api/user/categorize/senders/categorized/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/app/api/**/route.ts: Create GET API routes usingwithAuthorwithEmailAccountmiddleware inapps/web/app/api/*/route.ts, export response types asGetExampleResponsetype alias for client-side type safety
Always export response types from GET routes asGet[Feature]Responseusing type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions withnext-safe-actioninstead
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
**/app/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/get-api-route.mdc)
**/app/**/route.ts: Always wrap GET API route handlers withwithAuthorwithEmailAccountmiddleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes usingAwaited<ReturnType<typeof functionName>>pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses usingNextResponse.json()in GET API routes
Do not use try/catch blocks in GET API route handlers when usingwithAuthorwithEmailAccountmiddleware, as the middleware handles error handling
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
apps/web/app/**/[!.]*/route.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for route directories in Next.js App Router (e.g.,
api/hello-world/route)
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
apps/web/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,tsx}: API routes must usewithAuth,withEmailAccount, orwithErrormiddleware for authentication
All database queries must include user scoping withemailAccountIdoruserIdfiltering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throwSafeErrorinstead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields usingselectin database queries to prevent unintended information disclosure
Cron endpoints must usehasCronSecretorhasPostCronSecretto validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
**/app/api/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: usewithEmailAccountfor email-scoped operations, usewithAuthfor user-scoped operations, or usewithErrorwith proper validation for public/custom auth endpoints
UsewithEmailAccountmiddleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation usingemailAccountId
UsewithAuthmiddleware for user-level operations such as user settings, API keys, and referrals that use onlyuserId
UsewithErrormiddleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST usehasCronSecret()orhasPostCronSecret()validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret usinghasCronSecret(request)orhasPostCronSecret(request)and capture unauthorized attempts withcaptureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
**/{app,pages}/**/{route,+page}.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{app,pages}/**/{route,+page}.{ts,tsx}: Use middleware wrappers (withError, withAuth, withEmailAccount, withEmailProvider) that automatically create loggers with request context in API routes
Enrich logger context within route handlers using logger.with() to add request-specific fields like messageId
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
apps/web/app/api/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/*.ts: Create GET API routes wrapped withwithAuthorwithEmailAccountmiddleware for fetching data
Export response types from GET API routes usingexport type GetXResponse = Awaited<ReturnType<typeof getData>>
Files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
🧠 Learnings (16)
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/app/api/**/route.ts : Create GET API routes using `withAuth` or `withEmailAccount` middleware in `apps/web/app/api/*/route.ts`, export response types as `GetExampleResponse` type alias for client-side type safety
Applied to files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/api/**/*.ts : Create GET API routes wrapped with `withAuth` or `withEmailAccount` middleware for fetching data
Applied to files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
📚 Learning: 2025-11-25T14:37:11.434Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:11.434Z
Learning: Applies to **/app/**/route.ts : Always wrap GET API route handlers with `withAuth` or `withEmailAccount` middleware for consistent error handling and authentication in Next.js App Router
Applied to files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
📚 Learning: 2025-11-25T14:37:11.434Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:11.434Z
Learning: Applies to **/app/**/route.ts : Infer and export the response type for GET API routes using `export type GetResponse = Awaited<ReturnType<typeof getData>>` pattern in Next.js
Applied to files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
📚 Learning: 2025-11-25T14:37:22.822Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:22.822Z
Learning: Applies to **/app/**/route.ts : Infer and export response type for GET API routes using `Awaited<ReturnType<typeof functionName>>` pattern in Next.js
Applied to files:
apps/web/app/api/user/categorize/senders/categorized/route.ts
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/**/*.{ts,tsx} : Follow NextJS app router structure with (app) directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
🧬 Code graph analysis (5)
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsx (2)
apps/web/app/api/user/categorize/senders/progress/route.ts (1)
CategorizeProgress(5-7)apps/web/components/ProgressPanel.tsx (1)
ProgressPanel(8-64)
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx (5)
apps/web/utils/category.server.ts (1)
CategoryWithRules(4-11)apps/web/app/api/user/categorize/senders/categorized/route.ts (1)
CategorizedSendersResponse(6-8)apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsx (1)
BulkArchiveProgress(10-84)apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (1)
AutoCategorizationSetup(20-87)apps/web/components/BulkArchiveCards.tsx (1)
BulkArchiveCards(120-457)
apps/web/utils/actions/categorize.ts (3)
apps/web/utils/actions/safe-action.ts (1)
actionClient(74-129)apps/web/utils/categories.ts (1)
defaultCategory(1-105)apps/web/utils/path.ts (1)
prefixPath(1-4)
apps/web/app/api/user/categorize/senders/categorized/route.ts (2)
apps/web/utils/category.server.ts (1)
getUserCategoriesWithRules(24-39)apps/web/utils/middleware.ts (1)
withEmailAccount(408-426)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (3)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/utils/actions/categorize.ts (2)
createAllDefaultCategoriesAction(291-310)bulkCategorizeSendersAction(29-101)apps/web/components/new-landing/common/Card.tsx (3)
Card(67-105)CardHeader(22-54)CardContent(10-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (7)
apps/web/utils/actions/categorize.ts (1)
291-310: LGTM! Well-structured server action.The implementation correctly follows the server action patterns:
- Uses
actionClientwith proper metadata- Includes
emailAccountIdin database operations for proper scoping- Uses
skipDuplicatesto handle idempotent category creation- Revalidates the correct path after mutation
apps/web/app/api/user/categorize/senders/categorized/route.ts (1)
1-37: LGTM! Excellent adherence to API route patterns.This GET route follows all the coding guidelines:
- Wrapped with
withEmailAccountmiddleware for authentication and error handling- Response type properly exported using
Awaited<ReturnType<...>>pattern- Database queries correctly scoped to
emailAccountIdto prevent IDOR- Parallel data fetching with
Promise.allfor performanceBased on learnings, GET API routes should use
withAuthorwithEmailAccountmiddleware and export response types for client-side type safety.apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsx (1)
10-84: LGTM! Well-implemented progress tracking with smooth UX.The implementation demonstrates good practices:
- SWR polling controlled by categorization state
- Fake progress animation to provide responsive feedback
- Proper effect cleanup to prevent memory leaks
- Graceful completion handling with delay for user feedback
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx (1)
19-62: LGTM! Clean data orchestration with proper SWR patterns.The component correctly orchestrates the bulk archive flow:
- Uses SWR with
fallbackDatafor initial server-side data and conditional polling- Memoizes expensive computations appropriately
- Delegates to specialized child components for clean separation of concerns
- Properly handles data refresh on completion
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (3)
11-12: LGTM!The route configuration is appropriate for a dynamic, potentially long-running bulk archive operation.
14-20: LGTM!Correctly implements Next.js 15's async params API and performs ownership validation before data fetching.
22-55: LGTM!The parallel data fetching with
Promise.allis efficient, and the component composition correctly separates server and client boundaries usingClientOnlywrappers. Initial data is properly passed to the client component for hydration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/actions/categorize.ts">
<violation number="1" location="apps/web/utils/actions/categorize.ts:309">
P2: Return value `created` doesn't reflect actual records created when duplicates are skipped. Use the count returned by `createMany` for accurate reporting.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Fix all issues with AI Agents 🤖
In @apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:
- Line 10: The double cast "metadata as unknown as AlternativeComparisonProps"
bypasses TS safety; replace it by giving the MDX metadata a proper type or
validating it at runtime: import or declare a typed metadata (e.g.,
export/import a Metadata type from content.mdx) and use "satisfies
AlternativeComparisonProps" when assigning md in the Content function, or create
a runtime validator (e.g., Zod schema) and call schema.parse(metadata) to
produce md; update references to md, metadata, and AlternativeComparisonProps
accordingly.
In @apps/web/app/(marketing)/(landing)/creator/page.tsx:
- Around line 53-55: Replace the plain <a> wrapper with Next.js Link and avoid
nesting a native <button> inside an anchor: either (a) use Next.js Link (import
Link from "next/link") and render the interactive element as the anchor by
making the Button render as an anchor (e.g., if your Button supports an asChild
prop, pass asChild and wrap Button with Link) or (b) if Button cannot render as
an anchor, style the Link to look/behave like the button (give Link the same
classes/props) and render <Link
href="/case-studies/study/clicks-talent-saves-60-hours-weekly">…</Link> instead
of <a><Button/></a>, ensuring no <button> is nested inside an anchor and
preserving client-side navigation.
In @apps/web/app/(marketing)/(landing)/error.tsx:
- Line 9: The ErrorBoundary component currently types its props as any and omits
the reset function; update the signature of ErrorBoundary to accept a properly
typed props object (replace any with an interface or the Next.js
ErrorBoundaryProps type) that includes both error and reset (e.g., { error:
Error; reset: () => void } or import Next.js ErrorBoundaryProps), call or wire
the provided reset function from a retry UI control (e.g., a "Retry" button) so
clicking it invokes reset, and ensure the component uses the typed error for
rendering instead of any.
In @apps/web/app/(marketing)/blog/components/TableOfContents.tsx:
- Around line 18-38: The effect is re-running because the headings array is
re-created on every render; memoize the computed headings before the useEffect
(e.g., replace the ad-hoc filtered/derived headings with a useMemo that returns
the array of heading blocks and depends only on the stable inputs), then have
the useEffect depend on that memoized variable (the same name `headings` or
rename to `memoizedHeadings`) so IntersectionObserver is only recreated when
actual heading content changes; locate the code that builds `headings` and wrap
it in useMemo and update the useEffect dependency accordingly.
In @apps/web/app/(marketing)/blog/components/TryInboxZero.tsx:
- Around line 8-29: The Link currently wraps a Button, causing nested
interactive elements; remove the Button component inside TryInboxZero.tsx and
instead style the Link itself (or the anchor it renders) to look and behave like
the call-to-action: move the "w-full" and the Button's blue variant styles onto
the Link/anchor (and add an appropriate aria-label if needed), ensure the Link
is a block-level element that contains the image and copy, and remove the nested
focusable Button so there are no nested interactive controls.
In @apps/web/app/(marketing)/blog/page.tsx:
- Around line 261-268: The Image usage is using deprecated props layout and
objectFit; update the <Image> component (the instance rendering post.imageUrl
and alt={post.title}) to use the Next.js 13+ API by removing layout and
objectFit, adding the fill boolean prop, and apply object-fit via CSS or inline
style (e.g., className="object-cover" on the Image or style={{objectFit:
'cover'}}) while keeping the surrounding wrapper div with position/size so the
fill behavior works.
In @apps/web/app/(marketing)/blog/post/[slug]/Post.tsx:
- Around line 169-175: The external Link element rendering the author Twitter
profile (the <Link ... href={`https://x.com/${post.authorTwitter}`}
target="_blank`}> block) lacks rel attributes; add rel="noopener noreferrer" to
that Link so external links opened with target="_blank" cannot access
window.opener and follow security/accessibility best practices.
- Around line 119-131: The custom Portable Text mark renderer marks.link reads
value?.href which may be undefined and can cause broken links; update the
marks.link handler to derive href with a safe fallback (e.g., const href =
value?.href || '#') and pass a non-null string into the Link component, and
optionally add rel/target handling or skip rendering as a link when href is
falsy; change the link renderer in marks.link to compute the fallback href
before returning the Link so the component always receives a valid href.
In @apps/web/app/(marketing)/blog/post/alternatives-to-skiff-mail/content.mdx:
- Line 3: Fix the typo in the MDX frontmatter description: replace "aqcuired"
with "acquired" in the description string (the metadata key "description" in
content.mdx) so it reads "Notion recently acquired Skiff Mail and is sunsetting
it in six months. Here are some good alternatives to consider for your email
needs."
In @apps/web/app/(marketing)/blog/post/alternatives-to-skiff-mail/page.tsx:
- Around line 7-8: Fix the typo in the page metadata by updating the description
field string in page.tsx: change "aqcuired" to "acquired" in the description
property so it reads "Notion recently acquired Skiff Mail and is sunsetting it
in six months. Here are some good alternatives to consider for your email
needs."
In @apps/web/app/(marketing)/blog/post/bulk-unsubscribe-from-emails/content.mdx:
- Line 55: Replace the outdated "Gsuite" branding with "Google Workspace"
wherever it appears in the MDX content (specifically update the string "Gsuite"
found in the sentence beginning with "Support and Compatibility:" and the other
occurrence noted at line 71), ensuring both instances are changed to "Google
Workspace" and preserving the surrounding sentence wording and punctuation.
In
@apps/web/app/(marketing)/blog/post/how-my-open-source-saas-hit-first-on-product-hunt/content.mdx:
- Line 124: Fix the typo in the blog post sentence that currently reads
"monthtly" — replace "monthtly" with "monthly" so the sentence reads "...pay a
$50,000 monthly subscription for a SaaS product..." (locate the sentence in the
post content where the misspelling appears).
- Line 50: Replace the misspelled word "contributers" with the correct spelling
"contributors" in the blog post sentence and also change "its" to "it's" on the
same line as noted; update the MDX content string that contains the sentence
"Being open source also means anyone can contribute..." so the line reads
"...developers from all over the world contributing to making it better. Top
open source projects have hundreds of contributors." (and ensure "it's" is
corrected where indicated).
In
@apps/web/app/(marketing)/blog/post/inbox-zero-benefits-for-mental-health/page.tsx:
- Around line 17-18: The StructuredData component's headline prop contains a
typo ("Inbox Zero Benefitsfor Mental Health"); update the headline string passed
to StructuredData (the headline prop in the page file where StructuredData is
used) to "Inbox Zero Benefits for Mental Health" so the SEO structured data
contains the correct spacing.
In
@apps/web/app/(marketing)/blog/post/why-build-an-open-source-saas/content.mdx:
- Line 126: Fix the typo "monthtly" to "monthly" in the blog content string (the
sentence starting "But if a company is being asked to pay a $50,000 monthtly
subscription..."); update the MDX text to read "monthly" so the sentence becomes
"...pay a $50,000 monthly subscription for a SaaS product...".
- Line 52: Fix the typo in the blog paragraph: replace "contributers" with the
correct spelling "contributors" in the sentence "Top open source projects have
hundreds of contributers." in the content of the MDX file
(apps/web/app/(marketing)/blog/post/why-build-an-open-source-saas/content.mdx).
In @apps/web/app/(marketing)/case-studies/components/CaseStudy.tsx:
- Around line 73-82: The AvatarFallback logic assumes interviewee is a
non-empty, space-separated string and uses n[0], which can throw for empty or
undefined names; update the AvatarFallback expression in the CaseStudy component
to first guard for falsy interviewee, then split into words, filter out empty
strings (.filter(Boolean)), derive initials using safe indexing (e.g.,
word.charAt(0)), and limit the result to at most two characters (fallback to the
first two characters of the full name if no spaces) so initials generation is
robust.
In @apps/web/app/(marketing)/case-studies/components/CaseStudyCard.tsx:
- Around line 49-57: Replace the unstable array index key in CaseStudyCard's
keyResults mapping with a stable unique key: change key={index} to key={result}
in the JSX where keyResults.map((result, index) => ...) so each <div> uses the
result string as the key; if result may not be globally unique, combine it with
a stable identifier (e.g., `${result}-${someId}`) but prefer key={result} per
the review.
In @apps/web/app/(marketing)/case-studies/components/CaseStudyContent.tsx:
- Around line 51-57: The external Link component rendering the company link uses
target="_blank" without rel="noopener", which is a security risk; update the
Link element (the component rendering href={companyUrl}, target="_blank"} that
displays {company}) to include rel="noopener" (or rel="noopener noreferrer")
alongside target="_blank" so the opened page cannot access window.opener.
In @apps/web/app/(marketing)/sanity/lib/queries.ts:
- Around line 49-52: The GROQ query exported as postSlugsQuery is using the
wrong field name "date"; update the query inside postSlugsQuery to request
"publishedAt" instead of "date" so it matches the schema (modify the groq
template string in postSlugsQuery to include publishedAt).
- Around line 40-47: The GROQ queries reference a non-existent field named
"date"; update both recentPostsQuery and postSlugsQuery to use the schema's
datetime field "publishedAt" instead—replace order(date desc) with
order(publishedAt desc) and change any projected "date" entry to use publishedAt
(e.g., "date": publishedAt or rename the projection key to "publishedAt") so the
queries return the correct values.
In @apps/web/app/(marketing)/sanity/sanity.config.ts:
- Around line 1-31: The Sanity config references NEXT_PUBLIC_SANITY_PROJECT_ID /
NEXT_PUBLIC_SANITY_DATASET / NEXT_PUBLIC_SANITY_API_VERSION via the exported
symbols projectId, dataset, apiVersion; move the validation and exports for
these three vars out of the local sanity/env.ts into the main apps/web/env.ts
using a Zod schema under the client section (export client.projectId,
client.dataset, client.apiVersion), update imports in sanity.config.ts to use
those exports, delete or stop using the local sanity/env.ts, and then uncomment
and document these keys in .env.example and add them to turbo.json build
environment settings so the variables are centralized, validated, documented,
and included in the pipeline.
In @apps/web/app/(marketing)/sitemap.ts:
- Around line 103-119: The sitemap currently builds entries from hardcoded
staticUrls and from getBlogPosts(), which can produce duplicate URLs; update the
sitemap generation (the code that combines staticUrls with getBlogPosts()
results—look for the staticUrls variable and the function that calls
getBlogPosts() in apps/web/app/(marketing)/sitemap.ts) to deduplicate entries by
URL before returning: normalize URLs (trim/ensure consistent trailing
slash/lowercase if needed) and filter the combined array to keep only the first
occurrence of each url (e.g., using a Set or Map keyed by url) so duplicates are
removed regardless of source.
🟡 Minor comments (15)
apps/web/app/(marketing)/case-studies/components/CaseStudyCard.tsx-49-57 (1)
49-57: Replace array index with stable key.Using array indices as keys can cause rendering issues if the
keyResultsarray order changes. Since result strings should be unique within a case study, use the result text itself as the key.🔎 Proposed fix
- {keyResults.map((result, index) => ( + {keyResults.map((result) => ( <div - key={index} + key={result} className="flex items-center text-sm text-gray-700" >apps/web/app/(marketing)/case-studies/components/CaseStudy.tsx-73-82 (1)
73-82: Add defensive handling for initials generation.The fallback initials generation assumes
intervieweecontains space-separated words. If the name has no spaces or contains empty strings,n[0]could cause runtime errors.🔎 Proposed fix
<AvatarFallback> {interviewee .split(" ") + .filter((n) => n.length > 0) .map((n) => n[0]) .join("") - .toUpperCase()} + .toUpperCase() + .slice(0, 2)} </AvatarFallback>This filters empty strings and limits initials to 2 characters for better display.
apps/web/app/(marketing)/blog/post/how-my-open-source-saas-hit-first-on-product-hunt/content.mdx-124-124 (1)
124-124: Fix spelling error: "monthtly" → "monthly".🔎 Proposed fix
-But if a company is being asked to pay a $50,000 monthtly subscription for a SaaS product, the company may decide it's cheaper to self-host and not pay that fee. +But if a company is being asked to pay a $50,000 monthly subscription for a SaaS product, the company may decide it's cheaper to self-host and not pay that fee.apps/web/app/(marketing)/blog/post/how-my-open-source-saas-hit-first-on-product-hunt/content.mdx-50-50 (1)
50-50: Fix spelling error: "contributers" → "contributors".🔎 Proposed fix
-Open source projects also have strong communities built around them. More so than closed source products. As developers can meaningfully contribute to the project and impact its direction. And its important for contributors to have a place to collaborate. +Open source projects also have strong communities built around them. More so than closed source products. As developers can meaningfully contribute to the project and impact its direction. And it's important for contributors to have a place to collaborate.Note: Also fixed "its" → "it's" on the same line.
Committable suggestion skipped: line range outside the PR's diff.
apps/web/app/(marketing)/sitemap.ts-103-119 (1)
103-119: Potential duplicate blog URLs in sitemap.These hardcoded blog post URLs may be duplicated if the same posts are also returned by
getBlogPosts()from Sanity. Consider either:
- Removing these from
staticUrlsif they exist in Sanity, or- Deduplicating the final sitemap array by URL before returning.
🔎 Suggested deduplication
- return [...staticUrls, ...blogPosts]; + const allUrls = [...staticUrls, ...blogPosts]; + const seen = new Set<string>(); + return allUrls.filter((item) => { + if (seen.has(item.url)) return false; + seen.add(item.url); + return true; + });Committable suggestion skipped: line range outside the PR's diff.
apps/web/app/(marketing)/blog/post/alternatives-to-skiff-mail/page.tsx-7-8 (1)
7-8: Fix typo in description."aqcuired" should be "acquired".
🔎 Proposed fix
description: - "Notion recently aqcuired Skiff Mail and is sunsetting it in six months. Here are some good alternatives to consider for your email needs.", + "Notion recently acquired Skiff Mail and is sunsetting it in six months. Here are some good alternatives to consider for your email needs.",apps/web/app/(marketing)/blog/post/alternatives-to-skiff-mail/content.mdx-3-3 (1)
3-3: Fix typo in metadata description.The word "aqcuired" should be "acquired".
🔎 Proposed fix
- description: "Notion recently aqcuired Skiff Mail and is sunsetting it in six months. Here are some good alternatives to consider for your email needs.", + description: "Notion recently acquired Skiff Mail and is sunsetting it in six months. Here are some good alternatives to consider for your email needs.",apps/web/app/(marketing)/blog/post/why-build-an-open-source-saas/content.mdx-126-126 (1)
126-126: Typo: "monthtly" should be "monthly".-But if a company is being asked to pay a $50,000 monthtly subscription for a SaaS product, the company may decide it's cheaper to self-host and not pay that fee. +But if a company is being asked to pay a $50,000 monthly subscription for a SaaS product, the company may decide it's cheaper to self-host and not pay that fee.apps/web/app/(marketing)/blog/post/why-build-an-open-source-saas/content.mdx-52-52 (1)
52-52: Typo: "contributers" should be "contributors".-Being open source also means anyone can contribute to the project to improve it or fix bugs. Inbox Zero already has developers from all over the world contributing to making it better. Top open source projects have hundreds of contributers. +Being open source also means anyone can contribute to the project to improve it or fix bugs. Inbox Zero already has developers from all over the world contributing to making it better. Top open source projects have hundreds of contributors.apps/web/app/(marketing)/blog/post/inbox-zero-benefits-for-mental-health/page.tsx-17-18 (1)
17-18: Typo in headline: missing space between "Benefits" and "for".The StructuredData headline has a typo that will affect SEO structured data.
<StructuredData - headline="Inbox Zero Benefitsfor Mental Health" + headline="Inbox Zero Benefits for Mental Health" datePublished="2024-06-27T23:00:00+00:00"apps/web/app/(marketing)/blog/post/bulk-unsubscribe-from-emails/content.mdx-55-55 (1)
55-55: Consider updating "Gsuite" to current branding.Google rebranded G Suite to "Google Workspace" in 2020. Consider updating for accuracy:
-- **Support and Compatibility:** Currently, Inbox Zero supports Gmail and Gsuite email accounts, with plans to add other providers like Outlook in the future. +- **Support and Compatibility:** Currently, Inbox Zero supports Gmail and Google Workspace email accounts, with plans to add other providers like Outlook in the future.The same applies to line 71.
apps/web/app/(marketing)/blog/post/[slug]/Post.tsx-169-175 (1)
169-175: Addrel="noopener noreferrer"to external link.Per accessibility and security guidelines, external links with
target="_blank"should includerel="noopener noreferrer"to prevent the new page from accessingwindow.opener.🔎 Proposed fix
<Link href={`https://x.com/${post.authorTwitter}`} className="text-sm text-gray-500" target="_blank" + rel="noopener noreferrer" >apps/web/app/(marketing)/blog/post/[slug]/Post.tsx-119-131 (1)
119-131: Add fallback for potentially undefinedhref.
value?.hrefcould be undefined. Consider adding a fallback similar to the ReactMarkdown link handler.🔎 Proposed fix
marks: { link: ({ children, value }) => { - const href = value?.href; + const href = value?.href || "#"; return ( <Link href={href}apps/web/app/(marketing)/sanity/lib/queries.ts-49-52 (1)
49-52: Same field name inconsistency.The
postSlugsQueryalso referencesdatewhich should likely bepublishedAtto match the schema.🔎 Proposed fix
export const postSlugsQuery = groq`*[_type == "post"] { "slug": slug.current, - date + "date": publishedAt }`;apps/web/app/(marketing)/sanity/lib/queries.ts-40-47 (1)
40-47: Field name mismatch in GROQ queries:dateshould bepublishedAt.The
recentPostsQueryandpostSlugsQueryreference adatefield that doesn't exist in the postType schema. The schema definespublishedAtas the datetime field. This will cause these queries to return undefined values.🔎 Proposed fix
// Get 4 most recent posts -export const recentPostsQuery = groq`*[_type == "post"] | order(date desc) [0...4] { +export const recentPostsQuery = groq`*[_type == "post"] | order(publishedAt desc) [0...4] { "slug": slug.current, title, description, - date, + "date": publishedAt, "image": mainImage.asset->url }`; -export const postSlugsQuery = groq`*[_type == "post"] { +export const postSlugsQuery = groq`*[_type == "post"] { "slug": slug.current, - date + "date": publishedAt }`;Committable suggestion skipped: line range outside the PR's diff.
🧹 Nitpick comments (31)
apps/web/app/(marketing)/(landing)/block-cold-emails/page.tsx (1)
44-62: Consider using a semantic<section>element for the wrapper.The helper component is well-structured and correctly placed at the bottom of the file. The plain
<div>wrapper could optionally be a<section>element for improved HTML semantics, but this is a minor suggestion.Optional: Use semantic HTML
function ColdEmailBlockerFeatures() { return ( - <div> + <section> <OrganizedInbox title="Never read a cold email again" subtitle="Say goodbye to unsolicited outreach. Automatically filter sales pitches and cold emails so you only see messages that matter. Block out the noise and keep your inbox clean and focused on what matters." /> <Banner title="Customize what counts as a cold email"> Tell Inbox Zero what constitutes a cold email for you. It will block them based on your instructions. Automatically label cold emails so you can review them later if needed. </Banner> <StartedInMinutes title="Clean inbox, zero effort" subtitle="Set up your cold email blocker once and it works automatically. Keep your inbox clean and focused on what matters without manually filtering through spam and sales pitches." /> - </div> + </section> ); }apps/web/app/(marketing)/(landing)/layout.tsx (1)
3-7: Consider removingasyncif no asynchronous operations are performed.The function is marked
asyncbut doesn't useawaitor perform any asynchronous operations. Unless you're planning to add async data fetching later, marking this as a regular function would be more accurate and avoid unnecessary overhead.🔎 Proposed fix
-export default async function MarketingLandingLayout({ +export default function MarketingLandingLayout({ children, }: { children: React.ReactNode; }) {apps/web/app/(marketing)/(landing)/reply-zero-ai/page.tsx (1)
52-56: Consider tightening the Banner copy to eliminate redundancy.The Banner content repeats the title phrase "Never lose track of conversations" and contains similar phrasing about labeling emails. This could be more concise for better reader engagement.
💡 Suggested refinement
<Banner title="Never lose track of conversations"> - We label every email that needs a reply, so it's easy to focus on the - ones that matter. Never lose track of conversations. We label emails - awaiting replies and help you filter for overdue ones. + We label every email that needs a reply and help you filter for overdue + ones, so you can focus on what matters. </Banner>apps/web/app/(marketing)/(landing)/small-business/page.tsx (1)
54-80: Consider a semantic wrapper for better HTML structure.The outer
<div>wrapper could be replaced with a<section>element for better semantic HTML, or use a Fragment (<></>) if no wrapper styling is needed.🔎 Proposed semantic improvement
function SmallBusinessFeatures() { return ( - <div> + <section> <OrganizedInbox title="Never miss a customer or opportunity" subtitle="Every missed customer email is lost revenue. Our AI monitors your inbox 24/7, instantly identifying and prioritizing customer inquiries, sales opportunities, and important vendor communications so you can respond when it matters most. Automatically categorizes and prioritizes customer questions, complaints, and requests for faster response times." /> <Banner title="Work smarter, not harder"> Inbox Zero integrates seamlessly with your existing Gmail, so there's no learning curve. Just open your email and focus on growing your business while AI handles the routine communications. </Banner> <StartedInMinutes title="Deliver exceptional service at small business speed" subtitle="Inbox Zero learns your business style and suggests quick responses for customer inquiries, vendor communications, and partnership requests. Maintain professional relationships while focusing on what you do best - running your business." /> <Banner title="Scale your business without scaling your workload"> Focus on growth, not email management <br /> As your business grows, so does your email volume. Transform communication chaos into organized efficiency so you can serve more customers and capture more opportunities without working longer hours. </Banner> - </div> + </section> ); }apps/web/app/(marketing)/(landing)/enterprise/page.tsx (1)
19-48: LGTM! Well-structured page component.The component follows a clean composition pattern with proper mobile-first responsive design. The structure aligns well with other marketing landing pages in the codebase.
Optional: Consider content management for marketing copy
If marketing copy (subtitle, badge text) requires frequent updates, consider extracting these strings to constants or a headless CMS for easier content management without code changes. However, the current inline approach is acceptable and common for Next.js marketing pages.
apps/web/app/(marketing)/(landing)/bulk-email-unsubscriber/page.tsx (1)
53-71: Consider using semantic HTML for the wrapper.The wrapper
divcould be replaced with a<section>element or React Fragment for better semantic HTML structure.🔎 Proposed semantic improvement
function UnsubscribeFeatures() { return ( - <div> + <section> <OrganizedInbox title="Take back control of your inbox" subtitle="Effortlessly manage the newsletters in your inbox. One click unsubscribe, auto archive, or approve. No more drowning in marketing emails and newsletters you never signed up for." /> <Banner title="Bulk unsubscribe in seconds"> Review and unsubscribe from hundreds of newsletters at once. Inbox Zero identifies all your subscriptions and lets you clean them up with a single click. </Banner> <StartedInMinutes title="Clean inbox, minimal effort" subtitle="Set up automatic archiving for newsletters you want to keep but don't need cluttering your inbox. Or approve the ones you actually want to read. Your inbox, your rules." /> - </div> + </section> ); }apps/web/app/(marketing)/contact/page.tsx (1)
15-51: Overall implementation looks good, with a mobile responsiveness suggestion.The contact page is well-structured and follows Next.js patterns correctly. The layout, components, and email integration are all implemented properly.
However, consider making the padding responsive per coding guidelines:
The fixed padding
pb-40 pt-60(160px/240px) may be excessive on mobile devices. Consider using responsive variants to improve mobile UX:🔎 Proposed responsive padding adjustment
- <div className="pb-40 pt-60"> + <div className="pb-20 pt-32 md:pb-40 md:pt-60">As per coding guidelines: "Implement responsive design with Tailwind CSS using a mobile-first approach"
apps/web/app/(marketing)/case-studies/study/clicks-talent-saves-60-hours-weekly/content.mdx (1)
32-32: Consider polishing the marketing copy for professional tone.Static analysis identified several style opportunities in the testimonial quotes:
- Line 32: Consider replacing "stuff" with "content" or "emails" for a more professional tone
- Line 40: "really big budgets" could be "substantial budgets"
- Line 46: Remove filler phrases like "kind of" for more confident messaging
- Lines 92, 100: Replace "very easy/easily" with stronger alternatives like "straightforward" or "simple"
- Lines 106, 126: Use hyphens in compound modifiers ("full-time job", "time-saving")
These are direct quotes from the interviewee, so maintaining authenticity vs. polish is a judgment call.
Also applies to: 40-40, 46-46, 92-92, 100-100, 106-106, 126-126
apps/web/app/(marketing)/case-studies/components/CaseStudyCard.tsx (1)
66-66: Consider making read time dynamic.The "5 min read" text is hardcoded. If read times vary between case studies, consider adding a
readTimeprop toCaseStudyCardPropsor calculating it based on content length.apps/web/app/(marketing)/case-studies/page.tsx (1)
16-32: Consider data source scalability.The case studies are currently hardcoded. As you add more case studies, consider moving this data to a CMS (like Sanity, which is used elsewhere in the codebase) or a database to simplify content management.
apps/web/app/(marketing)/(alternatives)/best-perplexity-email-assistant-alternative/content.tsx (1)
9-20: Consider adding runtime validation for the metadata type assertion.The type assertion on Line 10 (
as unknown as AlternativeComparisonProps) is unsafe and could cause runtime errors if the MDX metadata doesn't match the expected shape. Consider using a Zod schema to validate the metadata before casting.🔎 Recommended approach using Zod validation
Define a Zod schema for AlternativeComparisonProps and validate the metadata:
+import { z } from "zod"; + +const alternativeComparisonPropsSchema = z.object({ + // Define the expected shape based on AlternativeComparisonProps + // Example fields: + // title: z.string(), + // description: z.string(), + // ... other fields +}); export function Content() { - const md = metadata as unknown as AlternativeComparisonProps; + const md = alternativeComparisonPropsSchema.parse(metadata); return ( <BlogLayout>This ensures type safety at runtime and provides clear error messages if the metadata shape is incorrect.
As per coding guidelines on Zod schema validation.
apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.mdx (1)
273-273: Inconsistent spelling: British vs American English.The text uses "customisation" (British spelling) in the feature comparison table, but the rest of the document appears to use American English. For consistency, consider using "customization" throughout.
🔎 Proposed fix
-| Full customisation/automation (e.g. forward / reply) | ✅ | ❌ | +| Full customization/automation (e.g. forward / reply) | ✅ | ❌ |apps/web/app/(marketing)/(alternatives)/components/AlternativeComparison.tsx (2)
88-102: Consider using stable keys instead of array indices.The pros and cons lists currently use array indices as keys, which goes against the coding guideline. While the impact is minimal for static lists like these, using content-based keys would be more robust.
🔎 Suggested approach for stable keys
<ul className="space-y-1"> - {pros.map((pro, index) => ( - <li key={index} className="text-sm text-gray-600"> + {pros.map((pro) => ( + <li key={pro} className="text-sm text-gray-600"> • {pro} </li> ))} </ul>Apply the same pattern to the cons list (lines 111-115). This assumes each pro/con string is unique, which is reasonable for marketing content.
Also applies to: 104-118
73-77: Consider making the savings message configurable.The "Save up to 50% with Inbox Zero" text is currently hardcoded. If different alternatives have different actual savings percentages, you might want to make this configurable via the props interface.
💡 Optional enhancement
Add an optional prop:
export interface AlternativeComparisonProps { // ... existing props inboxZeroPricing: string; + savingsMessage?: string; content: ReactNode;Then use it:
<div className="border-t pt-4"> <p className="text-sm text-gray-600"> - Save up to 50% with Inbox Zero + {savingsMessage ?? "Save up to 50% with Inbox Zero"} </p> </div>apps/web/app/(marketing)/blog/components/BlogPost.tsx (1)
5-11: Remove unused props or implement the commented date/author display.The
date,title, andauthorprops are declared but never used. The commented code on lines 16-21 suggests these were intended to be displayed but the implementation is incomplete.🔎 Suggested fixes
Option 1: Remove unused props
export function BlogPost(props: { - date: string; - title: string; - author: string; content: React.ReactNode; }) { const { content } = props;Option 2: Implement the date/author display
If you want to display the metadata, you'll need to:
- Import
formatandparseISOfromdate-fns- Uncomment the display code
+import { format, parseISO } from "date-fns"; import { Prose } from "@/app/(marketing)/blog/components/Prose"; import { BasicLayout } from "@/components/layouts/BasicLayout"; import { Card, CardContent } from "@/components/ui/card"; export function BlogPost(props: { date: string; title: string; author: string; content: React.ReactNode; }) { - const { content } = props; + const { date, author, content } = props; return ( <BasicLayout> <article className="mx-auto max-w-3xl px-6 py-20"> - {/* <div className="text-center"> + <div className="text-center"> <time dateTime={date} className="mb-1 text-xs text-gray-600"> {format(parseISO(date), "LLLL d, yyyy")} </time> <p className="text-sm font-semibold">by {author}</p> - </div> */} + </div>apps/web/app/(marketing)/sitemap.ts (1)
5-6: Top-level await for dynamic imports.Top-level await is supported in Next.js App Router, but these imports will execute at module load time regardless of whether Sanity credentials are configured. Consider moving these inside
getBlogPosts()to defer the import until needed and avoid potential initialization errors when Sanity is not configured.🔎 Suggested refactor
-const { sanityFetch } = await import("./sanity/lib/fetch"); -const { postSlugsQuery } = await import("./sanity/lib/queries"); - const logger = createScopedLogger("sitemap"); async function getBlogPosts() { // Skip Sanity fetch during build with dummy credentials or if marketing submodule is not available if ( !process.env.NEXT_PUBLIC_SANITY_PROJECT_ID || process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === "project123" ) { return []; // Return empty array directly } try { + const { sanityFetch } = await import("./sanity/lib/fetch"); + const { postSlugsQuery } = await import("./sanity/lib/queries"); + const posts = await sanityFetch<{ slug: string; date: string }[]>({apps/web/app/(marketing)/blog/post/alternatives-to-skiff-mail/content.mdx (1)
10-17: Consider minor style improvements for readability.A few optional style refinements:
- Line 10: Consider "until" instead of "till" for more formal tone
- Line 15: When "open source" is used as a compound adjective (modifying "email app"), it should be hyphenated: "open-source email app"
These are stylistic suggestions that can enhance the professional tone of the blog post.
apps/web/app/(marketing)/blog/components/ReadMore.tsx (1)
29-35: Consider adding lazy loading for improved performance.Since these images are likely below the fold in a "Read More" section, adding
loading="lazy"would improve initial page load performance by deferring image loading until they're needed.🔎 Proposed optimization
<Image src={post.image} alt={post.title} width={400} height={200} className="w-full object-cover" + loading="lazy" />apps/web/app/(marketing)/sanity/schemaTypes/authorType.ts (1)
10-20: Add required validation for critical author fields.The
nameandslugfields are essential for author documents but currently have no required validation. This could allow authors to be created without names or slugs, potentially breaking routing and display logic.🔎 Recommended validation
defineField({ name: "name", type: "string", + validation: (Rule) => Rule.required(), }), defineField({ name: "slug", type: "slug", options: { source: "name", }, + validation: (Rule) => Rule.required(), }),apps/web/app/(marketing)/blog/post/best-email-unsubscribe-app/content.mdx (1)
22-22: Optional: Consider hyphenating compound adjective.When "Open Source" is used as a compound adjective modifying "Option", it's conventionally hyphenated: "Open-Source Option". This is a minor style point and entirely optional.
apps/web/app/(marketing)/blog/post/inbox-zero-benefits-for-mental-health/content.mdx (1)
1-168: LGTM! Well-structured MDX content with proper metadata.The MDX file correctly exports metadata and provides comprehensive blog content. The static analysis tool flagged a minor prose style suggestion at line 162 ("regular routine" could be simplified to "routine"), but this is purely stylistic and optional.
apps/web/app/(marketing)/sanity/schemaTypes/blockContentType.ts (1)
34-34: Consider adding numbered list support.Currently only bullet lists are defined. If the blog content requires ordered lists, you may want to add support:
- lists: [{ title: "Bullet", value: "bullet" }], + lists: [ + { title: "Bullet", value: "bullet" }, + { title: "Numbered", value: "number" }, + ],apps/web/app/(marketing)/blog/components/TableOfContents.tsx (1)
13-16: Remove commented-out code or complete h3 support.The commented-out h3 filter (line 15) and the h3 conditional styling (line 48) are inconsistent. Since h3 headings are currently filtered out, the
heading.style === "h3"check on line 48 will never be true.Either remove the dead code or uncomment h3 support if needed.
apps/web/app/(marketing)/sanity/lib/image.ts (1)
1-11: LGTM! Clean image URL builder helper.This properly centralizes Sanity image URL generation. However, note that
Post.tsxcreates its own builder instance viaimageUrlBuilder(client)instead of using thisurlForhelper, resulting in duplicated instantiation.Consider using this
urlForhelper inPost.tsxto consolidate image URL generation:// In Post.tsx, replace: import imageUrlBuilder from "@sanity/image-url"; const builder = imageUrlBuilder(client); // With: import { urlFor } from "@/app/(marketing)/sanity/lib/image";apps/web/app/(marketing)/blog/post/[slug]/Post.tsx (2)
22-22: Consider using the centralizedurlForhelper.This creates a duplicate image URL builder instance. The
urlForhelper in@/app/(marketing)/sanity/lib/imagealready provides this functionality.🔎 Proposed refactor
-import imageUrlBuilder from "@sanity/image-url"; +import { urlFor } from "@/app/(marketing)/sanity/lib/image"; import { BlogLayout } from "@/components/layouts/BlogLayout"; ... -const builder = imageUrlBuilder(client);Then update usages like
builder.image(post.mainImage)tourlFor(post.mainImage).
87-103: Avoidconsole.errorin production code.Per coding guidelines, avoid using
console. Consider removing this logging or using the project's logger utility if error tracking is needed.🔎 Proposed fix
const decodeAssetId = (id: string) => { const match = pattern.exec(id); if (!match) { - console.error(`Invalid asset ID: ${id}`); return null; }apps/web/app/(marketing)/blog/post/email-management-best-practices/page.tsx (1)
1-28: LGTM! Standard blog post page structure.The page follows the established pattern for marketing blog posts with proper metadata exports and structured data.
Consider adding an image to the
StructuredDatacomponent for better SEO and social sharing. The empty arrayimage={[]}may result in missing image metadata in search results and social previews.apps/web/app/(marketing)/sanity/schemaTypes/postType.ts (1)
10-20: Consider adding validation for required fields.The
titleandslugfields are likely required for post functionality. Adding validation would prevent incomplete posts from being created in the CMS.🔎 Suggested validation
defineField({ name: "title", type: "string", + validation: (rule) => rule.required(), }), defineField({ name: "slug", type: "slug", options: { source: "title", }, + validation: (rule) => rule.required(), }),apps/web/app/(marketing)/blog/page.tsx (1)
248-250: Use a unique key instead of title.Using
post.titleas the key may cause issues if two posts have the same title. Consider using thefile(slug) field which should be unique.🔎 Proposed fix
{allPosts.map((post) => ( - <PostCard key={post.title} post={post} /> + <PostCard key={post.file} post={post} /> ))}apps/web/app/(marketing)/blog/post/[slug]/page.tsx (2)
77-79: UsenotFound()for proper 404 handling.Returning a simple
<div>when a post isn't found results in a 200 status code. Use Next.js'snotFound()function to properly trigger a 404 response and render the not-found page.🔎 Proposed fix
+import { notFound } from "next/navigation"; // ... other imports export default async function Page(props: Props) { const params = await props.params; const post = await sanityFetch<PostType>({ query: postQuery, params }); if (!post) { - return <div>Blog post content unavailable.</div>; + notFound(); } return <Post post={post} />; }
54-55: Biome suppression comment appears intentional.The
biome-ignorecomment for.fit("max")is flagged asnoFocusedTests, which seems like a miscategorized lint rule. This is a method chain on the image builder, not a test. The suppression works but the reason comment "blog" is unclear.Consider removing the biome-ignore if it's not actually triggering a lint error, or updating the comment to clarify why it's needed:
- // biome-ignore lint/suspicious/noFocusedTests: blog .fit("max")
apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(marketing)/case-studies/components/CaseStudyContent.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(marketing)/case-studies/study/clicks-talent-saves-60-hours-weekly/content.tsx
Outdated
Show resolved
Hide resolved
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Remove unused `categories` prop from BulkArchiveTab - Add guards for empty thread.messages arrays - Add error handling with try/catch in archiveSelected - Remove dead code for 'more emails' section - Fix createMany return value to use result.count - Use Next.js Link instead of anchor tag in creator page - Type error boundary props properly with reset function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Fix all issues with AI Agents 🤖
In @apps/web/app/(marketing)/(landing)/creator/page.tsx:
- Around line 14-19: The meta description in export const metadata: Metadata
(the description field) is too long (~225 chars); shorten it to ~150–160
characters to avoid truncation—edit the description value in page.tsx (export
const metadata) to a concise version that preserves the core value (AI assistant
managing brand emails, collaboration requests, fan inquiries) and target benefit
(save time/focus on creating content).
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 221-236: archiveCategory currently marks a category archived even
if addToArchiveSenderQueue fails partway; wrap the senders loop in a try/catch
(or catch individual addToArchiveSenderQueue calls) so failures are detected,
only call setArchivedCategories({ ... , [categoryName]: true }) after all
addToArchiveSenderQueue calls succeed, and on error log/report the failure and
avoid updating the archived state (consider setting an error/toast state to
surface the failure); refer to archiveCategory, addToArchiveSenderQueue, and
setArchivedCategories when making the change.
🧹 Nitpick comments (3)
apps/web/app/(marketing)/(landing)/error.tsx (1)
24-25: Optional: Consider consistent onClick handler style.For consistency with the
resetbutton handler, you could simplify line 25:🔎 Optional refactor for consistency
<Button onClick={reset}>Try again</Button> - <Button onClick={() => logOut()}>Log out</Button> + <Button onClick={logOut}>Log out</Button>Both forms work correctly, but passing the function directly is slightly more concise when no arguments are needed.
apps/web/components/BulkArchiveCards.tsx (1)
64-118: Consider moving helper function to bottom of file.Per coding guidelines, helper functions should be placed at the bottom of files. Consider moving
getCategoryIconafter the main exported component and child components.apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx (1)
23-33: Consider whether category rules are needed.The code fetches user categories including their rules (line 32), and then uses those categories in the emailGroups mapping (lines 54-56). However, the
BulkArchiveTabcomponent only accessescategory.nameand never uses therulesfield.If the rules are intended for future use, this is fine. Otherwise, you could simplify by using the category data already fetched with the newsletters (lines 29) instead of looking them up again, or modify
getUserCategoriesWithRulesto skip fetching rules for this use case.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/utils/actions/categorize.ts
🧰 Additional context used
📓 Path-based instructions (23)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/utils/actions/categorize.tsapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
apps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/utils/actions/*.ts: Usenext-safe-actionwith Zod schemas for all server actions (create/update/delete mutations), storing validation schemas inapps/web/utils/actions/*.validation.ts
Server actions should use 'use server' directive and automatically receive authentication context (emailAccountId) from theactionClient
apps/web/utils/actions/*.ts: Create corresponding server action implementation files using the naming conventionapps/web/utils/actions/NAME.tswith 'use server' directive
Use 'use server' directive at the top of server action implementation files
Implement all server actions using thenext-safe-actionlibrary with actionClient, actionClientUser, or adminActionClient for type safety and validation
UseactionClientUserwhen only authenticated user context (userId) is needed
UseactionClientwhen both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
UseadminActionClientfor actions restricted to admin users
Add metadata with a meaningful action name using.metadata({ name: "actionName" })for Sentry instrumentation and monitoring
Use.schema()method with Zod validation schemas from corresponding.validation.tsfiles in next-safe-action configuration
Access context (userId, emailAccountId, etc.) via thectxobject parameter in the.action()handler
UserevalidatePathorrevalidateTagfrom 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Files:
apps/web/utils/actions/categorize.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/actions/**/*.ts: Server actions must be located inapps/web/utils/actionsfolder
Server action files must start withuse serverdirective
apps/web/utils/actions/**/*.ts: Use proper error handling with try/catch blocks
Usenext-safe-actionwith Zod schemas for server actions to handle mutations
UserevalidatePathin server actions for cache invalidation after mutations
Files:
apps/web/utils/actions/categorize.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/actions/categorize.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/actions/categorize.ts
apps/web/utils/actions/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Infer types from Zod schemas using
z.infer<typeof schema>instead of duplicating as separate interfaces
Files:
apps/web/utils/actions/categorize.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/actions/categorize.ts
🧠 Learnings (48)
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure anchors have content that's accessible to screen readers
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't assign non-interactive ARIA roles to interactive HTML elements
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't assign interactive ARIA roles to non-interactive HTML elements
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use semantic elements instead of role attributes in JSX
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Give heading elements content that's accessible to screen readers (not hidden with `aria-hidden`)
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use valid, non-abstract ARIA roles for elements with ARIA roles
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : Use generic error messages instead of revealing internal details; throw `SafeError` instead of exposing user IDs, resource IDs, or system information
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsxapps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*Form*.tsx : Handle form submission results using `result?.serverError` to show error toasts and `toastSuccess` to show success messages after server action completion
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-07-08T13:14:03.250Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/api/user/stats/newsletters/route.ts:235-239
Timestamp: 2025-07-08T13:14:03.250Z
Learning: The user prefers proper TypeScript error type checking (using `error instanceof Error`) over type assertions with `as any` for better type safety and code quality.
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : For text inputs, use the `Input` component with `registerProps` for form integration and error handling
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.
Applied to files:
apps/web/app/(marketing)/(landing)/error.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/**/*.{ts,tsx} : Follow NextJS app router structure with (app) directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/**/[!.]*/route.{ts,tsx} : Use kebab-case for route directories in Next.js App Router (e.g., `api/hello-world/route`)
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Create corresponding server action implementation files using the naming convention `apps/web/utils/actions/NAME.ts` with 'use server' directive
Applied to files:
apps/web/utils/actions/categorize.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't have unused private class members
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't have unused labels
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't have unused variables
Applied to files:
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx
🧬 Code graph analysis (6)
apps/web/app/(marketing)/(landing)/creator/page.tsx (8)
apps/web/components/layouts/BasicLayout.tsx (1)
BasicLayout(7-15)apps/web/app/(landing)/home/Hero.tsx (2)
Hero(40-81)HeroVideoPlayer(83-126)apps/web/components/new-landing/common/WordReveal.tsx (1)
WordReveal(12-38)apps/web/components/new-landing/BrandScroller.tsx (1)
BrandScroller(15-49)apps/web/components/Banner.tsx (1)
Banner(8-24)apps/web/app/(landing)/home/FinalCTA.tsx (1)
FinalCTA(11-36)apps/web/components/new-landing/sections/OrganizedInbox.tsx (1)
OrganizedInbox(16-39)apps/web/components/new-landing/sections/PreWrittenDrafts.tsx (1)
PreWrittenDrafts(16-39)
apps/web/app/(marketing)/(landing)/error.tsx (2)
apps/web/components/ErrorDisplay.tsx (1)
ErrorDisplay(17-59)apps/web/utils/user.ts (1)
logOut(6-19)
apps/web/components/BulkArchiveCards.tsx (10)
apps/web/utils/category.server.ts (1)
CategoryWithRules(4-11)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/store/archive-sender-queue.ts (2)
addToArchiveSenderQueue(19-111)useArchiveSenderStatus(118-121)apps/web/utils/actions/categorize.ts (1)
removeAllFromCategoryAction(274-289)apps/web/components/Toast.tsx (1)
toastError(14-19)apps/web/components/ui/dropdown-menu.tsx (4)
DropdownMenu(185-185)DropdownMenuTrigger(186-186)DropdownMenuContent(187-187)DropdownMenuItem(188-188)apps/web/app/(app)/[emailAccountId]/smart-categories/CreateCategoryButton.tsx (1)
CreateCategoryDialog(115-137)apps/web/components/EmailCell.tsx (1)
EmailCell(3-26)apps/web/hooks/useThreads.ts (1)
useThreads(8-28)apps/web/utils/date.ts (1)
formatShortDate(25-56)
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx (3)
apps/web/utils/email-account.ts (1)
checkUserOwnsEmailAccount(5-22)apps/web/utils/category.server.ts (1)
getUserCategoriesWithRules(24-39)apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (1)
BulkArchiveTab(126-481)
apps/web/utils/actions/categorize.ts (3)
apps/web/utils/actions/safe-action.ts (1)
actionClient(74-129)apps/web/utils/categories.ts (1)
defaultCategory(1-105)apps/web/utils/path.ts (1)
prefixPath(1-4)
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (6)
apps/web/utils/category.server.ts (1)
CategoryWithRules(4-11)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/store/archive-sender-queue.ts (2)
addToArchiveSenderQueue(19-111)useArchiveSenderStatus(118-121)apps/web/components/EmailCell.tsx (1)
EmailCell(3-26)apps/web/hooks/useThreads.ts (1)
useThreads(8-28)apps/web/utils/date.ts (1)
formatShortDate(25-56)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (15)
apps/web/app/(marketing)/(landing)/error.tsx (1)
9-29: LGTM! Error boundary properly implemented.The error boundary now correctly:
- Uses properly typed props with
Error & { digest?: string }andresetfunction- Integrates Sentry error tracking via
useEffect- Provides user recovery with "Try again" button (calls
reset)- Offers escape hatch with "Log out" button
- Displays user-friendly error messages via
ErrorDisplayThis implementation follows Next.js error boundary conventions and all project guidelines.
apps/web/utils/actions/categorize.ts (1)
291-310: LGTM!The action correctly:
- Uses
actionClientwith proper metadata for Sentry instrumentation- Filters only enabled default categories
- Scopes all created categories to the authenticated
emailAccountId- Uses
skipDuplicates: truefor idempotent bulk creation- Returns the actual
result.countfrom Prisma'screateMany(addressing the previous review feedback)- Revalidates the correct path
apps/web/components/BulkArchiveCards.tsx (4)
459-525: LGTM!The
SenderRowcomponent properly implements:
- Keyboard accessibility with
role="button",tabIndex={0}, andonKeyDownhandler- Visual feedback for selection state with opacity changes
- Archive status display via
useArchiveSenderStatushook
527-554: LGTM!Clean status display component with appropriate visual indicators for each archive state.
556-648: LGTM!The
ExpandedEmailscomponent properly:
- Uses
useThreadshook for SWR-based data fetching (per coding guidelines)- Handles loading, error, and empty states appropriately
- Guards against empty
messagesarray (line 600) before accessing properties- Cleans email snippets by removing invisible characters and normalizing whitespace
1-56: LGTM!Imports follow coding guidelines:
- Specific lodash import (
import groupBy from 'lodash/groupBy') to minimize bundle size- Uses
@/path aliases consistently- All imports are at the top of the file
apps/web/app/(marketing)/(landing)/creator/page.tsx (3)
1-12: LGTM!Imports are well-organized, use proper
@/path aliases, and follow Next.js App Router conventions.
21-63: LGTM!The page component follows Next.js App Router conventions correctly. The composition is clean, with proper use of BasicLayout, Hero section with WordReveal animation, testimonial Banner, and CTA sections. All component props are properly typed and passed.
65-89: LGTM!The helper function is correctly positioned at the bottom of the file per coding guidelines. Component composition is clean and logical, with feature sections properly organized using OrganizedInbox, PreWrittenDrafts, and Banner components.
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx (1)
35-64: LGTM! Well-structured server component.The component correctly:
- Validates account ownership before rendering
- Fetches data in parallel for performance
- Uses ClientOnly boundaries for client-side components
- Transforms data appropriately for the BulkArchiveTab component
apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/BulkArchiveTab.tsx (5)
1-48: LGTM! Clean imports and type definitions.The imports follow project guidelines correctly:
- Specific imports from libraries
- Uses
@/path aliases- All imports at the top of the file
- "use client" directive properly placed
- Type definitions are clear and well-structured
50-124: LGTM! Clear placeholder logic with good documentation.The
getArchiveCandidatesfunction is well-documented as a placeholder for future AI-driven analysis. The keyword-based categorization provides reasonable fallback behavior, and theconfidenceConfigobject provides clean, consistent styling across confidence levels.
126-239: LGTM! Robust state management and error handling.The component properly:
- Pre-selects high/medium confidence senders by default
- Uses
useMemofor derived state to optimize re-renders- Includes comprehensive error handling in
archiveSelectedwith try/catch/finally- Displays user-friendly error messages via toast notifications
- Prevents UI state issues by resetting
isArchivingin the finally block
241-481: LGTM! Well-structured UI with proper accessibility.The rendering logic is excellent:
- Clear conditional states (success, empty, main)
- Proper keyboard navigation with
role="button",tabIndex={0}, andonKeyDownhandlers- Progress visualization with percentage calculation
- Responsive layout with Tailwind classes
- Clean component composition
483-672: LGTM! Robust sub-components with proper data fetching.The sub-components are well-implemented:
- SenderRow: Proper keyboard navigation and interaction handling
- ArchiveStatus: Clear status display for pending/processing/completed states
- ExpandedEmails:
- Uses
useThreadshook with SWR correctly (per coding guidelines)- Handles all states: loading, error, empty, and success
- Safely guards against empty messages array (line 625)
- Clean snippet formatting with zero-width character removal
All previously identified issues have been addressed.
- Memoize headings in TableOfContents to prevent IntersectionObserver recreation - Remove nested Button in TryInboxZero, style Link directly - Update deprecated Image props (layout/objectFit) to Next.js 13+ syntax - Add rel="noopener" to external link in CaseStudyContent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Fix all issues with AI Agents 🤖
In @apps/web/app/(marketing)/blog/page.tsx:
- Line 82: Correct the typo in the blog description string in
apps/web/app/(marketing)/blog/page.tsx by replacing "aqcuired" with "acquired"
in the literal that reads "Notion recently aqcuired Skiff Mail and is sunsetting
it in six months. Here are some good alternatives to consider for your email
needs."
- Around line 259-272: The group-hover utility on the h3 (className
"group-hover:text-gray-600") is inert because no ancestor has the "group" class;
add "group" to the parent element (e.g., the Card component's className on the
Card element that wraps Link and CardContent) so the hover variant becomes
active, or alternatively add "group" to the Link element if you prefer the Link
to be the hover target; ensure the chosen parent includes "group" alongside its
existing classes (e.g., "overflow-hidden transition-transform duration-300
hover:scale-105 group").
🧹 Nitpick comments (2)
apps/web/app/(marketing)/blog/page.tsx (2)
208-214: Consider adding error handling for Sanity fetch.The
sanityFetchcall lacks error handling. If the fetch fails, the page will crash. Consider wrapping in try-catch with a fallback to an empty array.Additionally, the magic string
"project123"should be extracted to a constant or environment variable for clarity.🔎 Proposed fix
+const DUMMY_SANITY_PROJECT_ID = "project123"; + export default async function BlogContentsPage() { // Skip Sanity fetch during build with dummy credentials let posts: SanityPost[] = []; - if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== "project123") { - posts = await sanityFetch<SanityPost[]>({ query: postsQuery }); + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== DUMMY_SANITY_PROJECT_ID) { + try { + posts = await sanityFetch<SanityPost[]>({ query: postsQuery }); + } catch (error) { + console.error("Failed to fetch Sanity posts:", error); + } }
248-250: Use a more stable identifier for the React key.Using
post.titleas the key could cause issues if titles are duplicated. Thepost.file(slug) is a more reliable unique identifier.- <PostCard key={post.title} post={post} /> + <PostCard key={post.file} post={post} />
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/(marketing)/blog/components/TableOfContents.tsxapps/web/app/(marketing)/blog/components/TryInboxZero.tsxapps/web/app/(marketing)/blog/page.tsxapps/web/app/(marketing)/case-studies/components/CaseStudyContent.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/app/(marketing)/blog/components/TryInboxZero.tsx
- apps/web/app/(marketing)/case-studies/components/CaseStudyContent.tsx
- apps/web/app/(marketing)/blog/components/TableOfContents.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(marketing)/blog/page.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(marketing)/blog/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(marketing)/blog/page.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(marketing)/blog/page.tsx
🧠 Learnings (8)
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to pages/_document.{jsx,tsx} : Don't import next/document outside of pages/_document.jsx in Next.js projects
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `next/image` package for images
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `<img>` elements in Next.js projects
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use `next/image` package for images
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
🧬 Code graph analysis (1)
apps/web/app/(marketing)/blog/page.tsx (3)
apps/web/components/layouts/BlogLayout.tsx (1)
BlogLayout(14-27)apps/web/components/new-landing/common/Card.tsx (2)
Card(67-105)CardContent(10-12)apps/web/components/new-landing/icons/Link.tsx (1)
Link(1-18)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (2)
apps/web/app/(marketing)/blog/page.tsx (2)
1-7: LGTM!Imports follow project conventions with
@/path aliases and usenext/imageandnext/linkas required.
9-27: LGTM!Type definitions are appropriately scoped to this file and not exported, following the coding guidelines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/components/BulkArchiveCards.tsx">
<violation number="1" location="apps/web/components/BulkArchiveCards.tsx:237">
P2: The caught `error` is unused. Consider logging it or including error details for debugging. If intentionally unused, prefix with underscore (`_error`) to satisfy linters.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI Agents
In @apps/web/app/(marketing)/blog/page.tsx:
- Around line 248-250: Replace the non-unique key usage in the allPosts.map
render to avoid React key collisions: change the key on the PostCard component
from post.title to the unique slug field post.file (i.e., in the
allPosts.map(...) where you render <PostCard key={post.title} post={post} />
swap to use key={post.file}); ensure post.file is present for all items or
fallback to a guaranteed-unique value before committing.
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 617-621: The Link that opens external emails (the Link wrapping
getEmailUrl(thread.id, userEmail, provider) with target="_blank") must
explicitly include rel="noopener noreferrer"; update the Link component props to
add rel="noopener noreferrer" to prevent the opened page from accessing
window.opener while keeping the href generated by getEmailUrl and preserving
existing classes and behavior.
🧹 Nitpick comments (7)
apps/web/components/BulkArchiveCards.tsx (3)
63-118: Helper function placement.Per coding guidelines, helper functions should be placed at the bottom of files rather than at the top. Consider moving
getCategoryIconbelow the component definitions.
221-242: Sequential await in loop may be intentional but worth noting.The
for...ofloop with sequentialawaitcalls processes senders one at a time. This is likely intentional for rate limiting, but if the queue can handle concurrent additions, consider usingPromise.allfor better performance with larger sender lists.🔎 Alternative with Promise.all (if concurrency is acceptable)
try { - for (const sender of selectedToArchive) { - await addToArchiveSenderQueue({ + await Promise.all( + selectedToArchive.map((sender) => + addToArchiveSenderQueue({ - sender: sender.address, - emailAccountId, - }); - } + sender: sender.address, + emailAccountId, + }), + ), + ); setArchivedCategories((prev) => ({ ...prev, [categoryName]: true }));
497-504: Type cast workaround.The double cast
e as unknown as React.MouseEventon line 501 is a workaround for the Checkbox component's event type. This works but is a code smell. Consider refactoring the handler to avoid the cast:🔎 Cleaner approach
<Checkbox checked={isSelected} - onClick={(e) => { - e.stopPropagation(); - onToggleSelection(e as unknown as React.MouseEvent); - }} + onCheckedChange={() => { + // Selection toggle doesn't need the event object + }} + onClick={(e) => e.stopPropagation()} className="size-5" />Or update
onToggleSelectionprop type to not require the event if it's only used forstopPropagation.apps/web/app/(marketing)/blog/page.tsx (4)
208-213: Consider a more semantic environment check.The hardcoded
"project123"string is used to detect dummy credentials during build. While functional, extracting this to a named constant or checking for a dedicated build-time flag would improve maintainability.🔎 Suggested improvements
Option 1: Extract to constant
+const DUMMY_SANITY_PROJECT_ID = "project123"; + export default async function BlogContentsPage() { // Skip Sanity fetch during build with dummy credentials let posts: SanityPost[] = []; - if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== "project123") { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== DUMMY_SANITY_PROJECT_ID) { posts = await sanityFetch<SanityPost[]>({ query: postsQuery }); }Option 2: Use a dedicated build flag
export default async function BlogContentsPage() { // Skip Sanity fetch during build with dummy credentials let posts: SanityPost[] = []; - if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== "project123") { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID && process.env.NODE_ENV !== "test") { posts = await sanityFetch<SanityPost[]>({ query: postsQuery }); }
228-228: Specify locale for consistent date formatting.Calling
toLocaleDateString()without a locale parameter uses the user's system locale, which can produce inconsistent date formats across different regions. Consider specifying a locale (e.g.,'en-US') for predictable formatting.🔎 Proposed fix
- date: new Date(post._createdAt).toLocaleDateString(), + date: new Date(post._createdAt).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }),This would produce dates like "Jan 5, 2024" consistently, matching the format used in the
mdxPostsarray.
277-283: Consider descriptive alt text for author image.While
alt=""is technically acceptable here since the author's name appears adjacent to the image (making it decorative/redundant), providing descriptive alt text likealt={post.author.name}can enhance the screen reader experience by explicitly identifying the image subject.🔎 Proposed enhancement
<Image src={post.author.imageUrl} - alt="" + alt={post.author.name} className="h-8 w-8 rounded-full bg-gray-50" width={32} height={32} />
29-204: Consider extracting static blog data to a separate file.The
mdxPostsarray spans 175 lines. Moving it to a dedicated data file (e.g.,apps/web/app/(marketing)/blog/data/mdx-posts.ts) would improve file organization and make the component logic easier to scan.This is purely organizational—no functional changes needed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsxapps/web/components/BulkArchiveCards.tsx
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(marketing)/blog/page.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsx
🧠 Learnings (27)
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsxapps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/app/(marketing)/(landing)/enterprise/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure anchors have content that's accessible to screen readers
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't assign non-interactive ARIA roles to interactive HTML elements
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't assign interactive ARIA roles to non-interactive HTML elements
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use semantic elements instead of role attributes in JSX
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Give heading elements content that's accessible to screen readers (not hidden with `aria-hidden`)
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use valid, non-abstract ARIA roles for elements with ARIA roles
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Applied to files:
apps/web/app/(marketing)/(landing)/creator/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `next/image` package for images
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `<img>` elements in Next.js projects
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use `next/image` package for images
Applied to files:
apps/web/app/(marketing)/blog/page.tsx
🧬 Code graph analysis (3)
apps/web/app/(marketing)/(landing)/creator/page.tsx (7)
apps/web/app/(marketing)/(landing)/enterprise/page.tsx (1)
metadata(11-16)apps/web/app/(landing)/home/Hero.tsx (2)
Hero(40-81)HeroVideoPlayer(83-126)apps/web/components/new-landing/BrandScroller.tsx (1)
BrandScroller(15-49)apps/web/components/Banner.tsx (1)
Banner(8-24)apps/web/app/(landing)/home/FinalCTA.tsx (1)
FinalCTA(11-36)apps/web/components/new-landing/sections/OrganizedInbox.tsx (1)
OrganizedInbox(16-39)apps/web/components/new-landing/sections/PreWrittenDrafts.tsx (1)
PreWrittenDrafts(16-39)
apps/web/app/(marketing)/(landing)/enterprise/page.tsx (9)
apps/web/app/(marketing)/(landing)/creator/page.tsx (1)
metadata(14-19)apps/web/components/layouts/BasicLayout.tsx (1)
BasicLayout(7-15)apps/web/app/(landing)/home/Hero.tsx (2)
Hero(40-81)HeroVideoPlayer(83-126)apps/web/components/new-landing/common/WordReveal.tsx (1)
WordReveal(12-38)apps/web/components/new-landing/BrandScroller.tsx (1)
BrandScroller(15-49)apps/web/app/(landing)/home/FAQs.tsx (1)
FAQs(66-98)apps/web/components/new-landing/sections/OrganizedInbox.tsx (1)
OrganizedInbox(16-39)apps/web/components/Banner.tsx (1)
Banner(8-24)apps/web/components/new-landing/sections/StartedInMinutes.tsx (1)
StartedInMinutes(34-139)
apps/web/app/(marketing)/blog/page.tsx (2)
apps/web/components/layouts/BlogLayout.tsx (1)
BlogLayout(14-27)apps/web/components/new-landing/icons/Link.tsx (1)
Link(1-18)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (12)
apps/web/app/(marketing)/(landing)/enterprise/page.tsx (4)
1-9: LGTM! Clean import structure.All imports follow project conventions with
@/path aliases and are organized logically.
11-16: LGTM! Proper SEO metadata.The metadata follows the established pattern from other marketing pages with appropriate title, description, and canonical URL.
18-47: LGTM! Well-structured page component.The component follows React best practices with clean composition of imported components. The Hero section is properly configured with WordReveal animation, and the layout structure matches established patterns in the codebase.
49-79: LGTM! Properly structured helper component.The
EnterpriseFeaturesfunction is correctly placed at the bottom of the file and appropriately kept private to the module. The component composition is clean, and all props passed to child components match their expected signatures.apps/web/components/BulkArchiveCards.tsx (4)
1-61: LGTM!Imports follow project conventions: specific lodash import (
groupBy),@/path aliases, and the internalEmailGrouptype is correctly not exported since it's only used within this file.
305-377: LGTM!The category card rendering is well-implemented with proper accessibility attributes (
role="button",tabIndex={0}, keyboard event handlers), correct button types, and appropriate event propagation handling.
533-560: LGTM!The
ArchiveStatuscomponent cleanly handles all status cases with appropriate visual feedback.
604-606: Null check properly added.Good defensive coding with the
if (!firstMessage) return null;guard. This prevents the potential crash whenthread.messagesis empty.apps/web/app/(marketing)/(landing)/creator/page.tsx (4)
1-12: LGTM! Imports are properly structured.All imports are at the top of the file, use path aliases correctly, and the type-only import for
Metadatauses the properimport typesyntax.
14-19: LGTM! Metadata is well-structured and optimized.The metadata follows Next.js conventions with a descriptive title, properly sized description (~146 characters—within the optimal SEO range), and canonical URL. The previous meta description length concern has been appropriately addressed.
21-63: LGTM! Component structure is well-organized.The
CreatorPagecomponent properly uses Next.js conventions as a server component, follows the established marketing page composition pattern, and correctly implements client-side navigation with Next.jsLink(lines 54-56). The previous navigation concern has been appropriately resolved.
65-89: LGTM! Helper component follows established patterns.The
CreatorFeaturescomponent is properly positioned at the bottom of the file, not exported (internal-only use), and correctly composes the marketing sections using the imported UI components.
The marketing directory was accidentally converted from a submodule to a regular directory during a merge, causing 100+ spurious file changes in the PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Move category icon mapping logic to bulk-archive/categoryIcons.ts to reduce BulkArchiveCards.tsx file size. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI Agents
In @apps/web/components/BulkArchiveCards.tsx:
- Line 164: The catch (error) in the BulkArchiveCards.tsx component is currently
ignoring the captured error; update the catch to either log the error and
include its message in the user toast or rename it to _error if intentionally
unused: call console.error(error) (or your app logger) and change the
toast/error notification to include error.message (or String(error)) for clearer
debugging, or simply rename the parameter to _error to indicate it is
intentionally unused.
- Around line 544-547: The Link element rendering the email link in
BulkArchiveCards.tsx (the Link that uses getEmailUrl(thread.id, userEmail,
provider) and target="_blank") must include rel="noopener noreferrer"; update
that Link JSX to add the rel attribute so external pages opened with
target="_blank" cannot access window.opener and satisfy the project's security
guideline.
🧹 Nitpick comments (1)
apps/web/components/BulkArchiveCards.tsx (1)
156-161: Consider parallel execution for better performance (optional).The current implementation uses sequential
awaitin a loop, which processes senders one at a time. If rate limiting isn't a concern foraddToArchiveSenderQueue, usingPromise.all()could significantly improve performance for categories with many senders.🔎 Alternative implementation with Promise.all
try { - for (const sender of selectedToArchive) { - await addToArchiveSenderQueue({ - sender: sender.address, - emailAccountId, - }); - } + await Promise.all( + selectedToArchive.map((sender) => + addToArchiveSenderQueue({ + sender: sender.address, + emailAccountId, + }) + ) + ); setArchivedCategories((prev) => ({ ...prev, [categoryName]: true }));Note: Only apply this if the archive queue can handle concurrent requests. Sequential processing may be intentional to prevent rate limiting or resource exhaustion.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/components/bulk-archive/categoryIcons.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/components/bulk-archive/categoryIcons.ts
🧠 Learnings (6)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (8)
apps/web/components/bulk-archive/categoryIcons.ts (1)
22-76: LGTM! Clean utility implementation.The icon mapping logic is clear, maintainable, and covers a comprehensive set of categories. The cascading if-return pattern with case-insensitive matching is appropriate for this use case, and the default fallback ensures robustness.
apps/web/components/BulkArchiveCards.tsx (7)
1-45: LGTM! Imports and type definitions are well-organized.The imports follow project guidelines, including the specific lodash function import to minimize bundle size. The
EmailGrouptype is appropriately scoped for this component.
47-100: LGTM! State management and memoization are well-structured.The component properly uses
useMemowith correct dependencies, and the state organization is logical for managing the complex UI interactions (expansion, selection, archival status). ThegroupedEmailsmemoization ensures empty categories are still displayed, which provides good user feedback.
193-389: LGTM! Render logic with excellent accessibility support.The component properly implements keyboard navigation with
role="button",tabIndex, andonKeyDownhandlers for interactive elements. The UI gracefully handles both archived and active states, and the expanded/collapsed pattern provides good progressive disclosure. Event propagation is correctly managed withstopPropagationwhere needed.
392-458: LGTM! SenderRow properly handles interaction states.The component correctly implements keyboard accessibility and manages selection state. The opacity change for unselected senders provides clear visual feedback. The type casting on line 428 is not ideal but is a pragmatic solution for the Checkbox component's event handler type requirements.
460-487: LGTM! Clear status feedback.The
ArchiveStatuscomponent provides appropriate visual feedback for each stage of the archive process. The switch statement handles all expected states with sensible defaults.
504-533: LGTM! Proper guards and state handling.The component correctly handles loading, error, and empty states with appropriate UI feedback. The guard check for
firstMessageon line 533 prevents potential crashes when threads have empty message arrays.
558-567: LGTM! Robust snippet sanitization.The snippet cleaning logic properly handles invisible characters, zero-width spaces, and whitespace normalization before truncation. This prevents rendering issues and provides consistent UI display.
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/components/SetupCard.tsx (1)
36-43: Consider optimizing the image.The
unoptimizedprop disables Next.js automatic image optimization. Unless there's a specific reason (e.g., SVG animations or external URL issues), consider removing it to benefit from Next.js image optimization features like lazy loading and automatic format selection.💡 Suggested change
<Image src={imageSrc} alt={imageAlt} width={200} height={200} className="mx-auto dark:brightness-90 dark:invert" - unoptimized />
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/web/public/images/illustrations/working-vacation.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/components/SetupCard.tsxapps/web/next.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/SetupCard.tsxapps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/SetupCard.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/SetupCard.tsxapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/SetupCard.tsxapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/SetupCard.tsxapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/components/SetupCard.tsxapps/web/next.config.tsapps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/next.config.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/next.config.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
🧠 Learnings (29)
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/components/SetupCard.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Ensure responsive design with mobile-first approach in components
Applied to files:
apps/web/components/SetupCard.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Prefer functional components with hooks in React
Applied to files:
apps/web/components/SetupCard.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use shadcn/ui components when available
Applied to files:
apps/web/components/SetupCard.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use `next/image` package for images
Applied to files:
apps/web/next.config.ts
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `next/image` package for images
Applied to files:
apps/web/next.config.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `<img>` elements in Next.js projects
Applied to files:
apps/web/next.config.ts
📚 Learning: 2026-01-05T13:44:53.648Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1199
File: apps/web/utils/meeting-briefs/send-briefing.ts:0-0
Timestamp: 2026-01-05T13:44:53.648Z
Learning: In apps/web/utils/meeting-briefs/, the Zod schema in generate-briefing.ts intentionally only includes `{guests: GuestBriefing[]}` because the AI only generates guest briefings. The `internalTeamMembers` field comes from domain-based filtering in gather-context.ts (not AI-researched) and is merged into the BriefingContent in send-briefing.ts before sending the email. This separation keeps the Zod schema focused on what the AI actually generates.
<!-- [add_learning]
In the meeting briefing feature (apps/web/utils/meeting-briefs/), there's an intentional type separation: the Zod schema BriefingContent type from generate-briefing.ts differs from the email package BriefingContent type in packages/resend/emails/meeting-briefing.tsx. The AI-generated content uses a narrower type while the email content includes additional domain-filtered data merged in send-briefing.ts.
Applied to files:
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/*/page.tsx : Pages must be Server components that load data directly
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components so you can load data into them directly
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't reassign exceptions in catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsx (2)
apps/web/components/SetupCard.tsx (1)
SetupCard(19-69)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (1)
ConnectCalendar(13-124)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (16)
apps/web/next.config.ts (1)
61-64: Configuration change is correct and properly configured.The domain
illustrations.popsy.cois actively used in the codebase viacelebration.ts, which exportsgetCelebrationImage(). This utility is imported by theCelebrationcomponent, which usesnext/imageto load the illustrations and is integrated into theEmailListcomponent. The remote pattern configuration is properly formatted, follows the existing pattern structure, and uses HTTPS for security.apps/web/app/(app)/[emailAccountId]/briefs/Onboarding.tsx (2)
3-25: LGTM! Clean refactoring to use shared SetupCard component.The extraction of features into a static array is a good pattern for maintainability. The component now uses the shared SetupCard component, reducing code duplication.
27-63: LGTM! Proper use of SetupCard with conditional content.The component correctly uses SetupCard's children prop to render different content based on whether the calendar is connected, maintaining the original behavior while simplifying the UI structure.
apps/web/components/SetupCard.tsx (1)
1-17: LGTM! Clean type definitions and imports.The FeatureItem type is well-structured, and all necessary dependencies are properly imported. Using ReactNode for the icon provides good flexibility for different icon types.
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (3)
1-29: LGTM! Well-structured setup with clear feature descriptions.The static features array clearly communicates the value proposition of the bulk archive feature to users.
37-61: LGTM! Proper error handling and state management.The enableFeature callback correctly handles the async operation with proper loading states and error handling. The past review concern about redundant server actions has been addressed—this now makes a single call to
bulkCategorizeSendersAction.
63-76: LGTM! Clean setup UI using shared component.The component properly uses SetupCard with appropriate props and includes a loading state on the action button for good UX.
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (3)
1-12: LGTM! Appropriate route configuration.The
force-dynamicsetting is correct for user-specific data, and the 5-minutemaxDurationprovides sufficient time for bulk operations.
14-38: LGTM! Efficient server-side data fetching with proper authorization.The page correctly:
- Uses Next.js 15's async params/searchParams API
- Performs ownership validation before data access
- Fetches data in parallel for optimal performance
- Determines onboarding state based on both query params and data availability
40-70: LGTM! Clean conditional rendering with proper client/server boundaries.The page correctly wraps client components in
ClientOnlyand provides appropriate layouts for both onboarding and normal states. Initial server-fetched data is properly passed down to client components.apps/web/components/BulkArchiveCards.tsx (6)
1-34: LGTM! Clean imports following project guidelines.Good use of specific lodash imports (
import groupBy from 'lodash/groupBy') to minimize bundle size, as per coding guidelines.
36-90: LGTM! Well-structured state management and memoization.The component properly uses
useMemofor derived data (categoryMap, groupedEmails) and maintains clean separation of concerns with multiple focused state hooks. The sorting logic appropriately handles the "Uncategorized" category.
134-155: LGTM! Error handling properly implemented.The past review concerns have been addressed:
- Try/catch block ensures errors are caught
- Category is only marked as archived after all senders are successfully queued
- Error variable properly prefixed with underscore to indicate intentional non-use
- Toast notification provides user feedback on failure
Note: The sequential
awaitin the loop may impact performance for large sender lists, but this could be intentional for rate limiting.
195-241: LGTM! Excellent accessibility implementation.The category header properly implements:
- Keyboard navigation with
onKeyDownhandling Enter/Space- Semantic
role="button"withtabIndex={0}- Visual hover states
- Event propagation control
This follows accessibility best practices per coding guidelines.
276-371: LGTM! Clean component implementations with proper state handling.Both
SenderRowandArchiveStatusare well-implemented:
- Proper accessibility with keyboard navigation
- Clear visual feedback for different archive states
- Correct event handling with stopPropagation
373-466: LGTM! All past review issues have been addressed.The
ExpandedEmailscomponent now correctly handles:
- Null check for
firstMessagepreventing crashes (line 417)- Security with
rel="noopener noreferrer"on external links (line 431)- Proper loading, error, and empty states
- Thorough snippet text cleaning for display
The dead code issue ("+N more emails" section) mentioned in past reviews has also been removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts">
<violation number="1" location="apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts:11">
P2: The prompt still instructs the AI to use "Unknown" (lines 78 and 85), but the `UNKNOWN_CATEGORY` constant was changed to "Other". This creates an inconsistency - either the prompt should be updated to use "Other" (or interpolate the constant), or keep the constant as "Unknown".</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In @apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx:
- Around line 30-36: Destructure the error from the useSWR call in
BulkArchiveContent (e.g., const { data, mutate, error } = useSWR(...)) and add
an effect/handler that, when error is truthy, calls toastError with the server
error payload (use data?.serverError or result?.serverError per guideline) to
surface fetch failures; ensure you import toastError from "@/components/Toast"
and avoid duplicate toasts by guarding so the toast only runs when the error
first appears.
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 32-35: You duplicated the EmailGroup type in BulkArchiveCards.tsx;
instead remove the local type and import the exported EmailGroup type from the
module that defines archive candidate utilities (the module that exports
EmailGroup from get-archive-candidates), updating the top-level imports in
BulkArchiveCards.tsx and replacing the local declaration; ensure you import it
as a type-only import (e.g., import type { EmailGroup } from '...') and adjust
any usages to reference the imported EmailGroup.
- Around line 385-412: Replace the manual loading/error conditional block in
BulkArchiveCards (the useThreads call and subsequent isLoading/error checks)
with the LoadingContent component: call useThreads(...) as before, then render
<LoadingContent loading={isLoading} error={error}> around the normal UI; move
the "No emails found" / thread list rendering into LoadingContent's children so
LoadingContent handles the skeleton and error state consistently. Ensure you
import LoadingContent and preserve the existing props passed to useThreads
(sender, limit, type) and the existing border-t/bg-muted container styling when
migrating the UI into LoadingContent.
- Around line 311-318: The Checkbox click handler is using a double cast (as
unknown as React.MouseEvent) which bypasses TypeScript safety; update the types
so you pass the real event type instead of force-casting: adjust the Checkbox
onClick signature or change the onToggleSelection parameter type in the
SenderRow props to accept the event type emitted by Checkbox (e.g.,
React.MouseEvent<HTMLButtonElement> or the correct element/event generic), and
then call onToggleSelection(e) without casting and remove e.stopPropagation from
the cast workaround if not needed.
In @apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts:
- Line 11: The prompt text still tells the LLM to return "Unknown" while the
constant UNKNOWN_CATEGORY was changed to "Other"; update the prompt occurrences
that instruct the model to use "Unknown" so they instead reference the
UNKNOWN_CATEGORY value or the string "Other" (the two prompt spots in
ai-categorize-senders.ts where the LLM is explicitly told to return "Unknown"),
ensuring the returned label matches the UNKNOWN_CATEGORY constant used later in
the code.
🧹 Nitpick comments (3)
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx (1)
33-33: Consider whether 2-second polling interval is appropriate.The component polls every 2 seconds (
refreshInterval: 2000) while categorization is in progress. Depending on the categorization workload and expected duration, this could be aggressive. Consider whether a longer interval (e.g., 5000ms) would be sufficient while reducing server load.apps/web/components/BulkArchiveCards.tsx (2)
145-150: Consider parallel processing for better performance.The sequential
awaitin the loop processes senders one at a time, which may be slow for categories with many senders. Unless sequential processing is required for rate limiting, consider usingPromise.all()for parallel execution.⚡ Proposed optimization
try { - for (const sender of selectedToArchive) { - await addToArchiveSenderQueue({ - sender: sender.address, - emailAccountId, - }); - } + await Promise.all( + selectedToArchive.map((sender) => + addToArchiveSenderQueue({ + sender: sender.address, + emailAccountId, + }) + ) + ); setArchivedCategories((prev) => ({ ...prev, [categoryName]: true }));
446-456: Extract snippet cleaning logic to a helper function.The inline snippet cleaning logic using an IIFE makes the render logic harder to read and test. Consider extracting it to a named helper function at the bottom of the file for better code organization.
♻️ Suggested refactor
Add at the bottom of the file:
function cleanSnippet(snippet: string, maxLength: number = 80): string { // Remove invisible/zero-width chars and normalize whitespace const cleaned = snippet .replace(/[\u034F\u200B-\u200D\uFEFF\u00A0]/g, "") .trim() .replace(/\s+/g, " "); return cleaned.length > maxLength ? `${cleaned.slice(0, maxLength).trimEnd()}...` : cleaned; }Then simplify the render:
{snippet && ( <span className="ml-2 text-muted-foreground"> - {(() => { - // Remove invisible/zero-width chars and normalize whitespace - const cleaned = snippet - .replace(/[\u034F\u200B-\u200D\uFEFF\u00A0]/g, "") - .trim() - .replace(/\s+/g, " "); - return cleaned.length > 80 - ? `${cleaned.slice(0, 80).trimEnd()}...` - : cleaned; - })()} + {cleanSnippet(snippet)} </span> )}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/BulkArchiveCards.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.ts
🧰 Additional context used
📓 Path-based instructions (29)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
LLM-related code must be organized in specific directories:
apps/web/utils/ai/for main implementations,apps/web/utils/llms/for core utilities and configurations, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.ts
apps/web/utils/ai/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.ts: LLM feature functions must import fromzodfor schema validation, usecreateScopedLoggerfrom@/utils/logger,chatCompletionObjectandcreateGenerateObjectfrom@/utils/llms, and importEmailAccountWithAItype from@/utils/llms/types
LLM feature functions must follow a standard structure: accept options withinputDataandemailAccountparameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and usecreateGenerateObjectto execute the LLM call
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures usingwithRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/categories.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsxapps/web/utils/categories.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/categories.ts
apps/web/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
apps/web/__tests__/**/*.test.ts: Place all LLM-related tests inapps/web/__tests__/directory
Use vitest imports (describe,expect,test,vi,beforeEach) in LLM test files
Mock 'server-only' module with empty object in LLM test files:vi.mock("server-only", () => ({}))
Set timeout constantconst TIMEOUT = 15_000;for LLM tests
Usedescribe.runIf(isAiTest)with environment variableRUN_AI_TESTS === "true"to conditionally run LLM tests
Useconsole.debug()for outputting generated LLM content in tests, e.g.,console.debug("Generated content:\n", result.content);
Prefer using existing helpers from@/__tests__/helpers.ts(getEmailAccount,getEmail,getRule,getMockMessage,getMockExecutedRule) instead of creating custom test data helpers
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
**/{scripts,tests,__tests__}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
apps/web/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
Place AI tests in the
__tests__directory and do not run them by default as they use a real LLM
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/notes.mdc)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/__tests__/ai-categorize-senders.test.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsxapps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/bulk-archive/categoryIcons.tsapps/web/components/BulkArchiveCards.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
🧠 Learnings (21)
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Place AI tests in the `__tests__` directory and do not run them by default as they use a real LLM
Applied to files:
apps/web/__tests__/ai-categorize-senders.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests
Applied to files:
apps/web/__tests__/ai-categorize-senders.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/__tests__/ai-categorize-senders.test.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/__tests__/ai-categorize-senders.test.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/__tests__/ai-categorize-senders.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) to wrap `useSWR` for individual API endpoints
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : Use kebab-case for PostHog feature flag keys (e.g., `inbox-cleaner`, `pricing-options-2`)
Applied to files:
apps/web/components/bulk-archive/categoryIcons.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't reassign exceptions in catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
🧬 Code graph analysis (1)
apps/web/components/BulkArchiveCards.tsx (11)
apps/web/utils/bulk-archive/get-archive-candidates.ts (1)
EmailGroup(3-6)apps/web/utils/category.server.ts (1)
CategoryWithRules(4-11)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/utils/categories.ts (1)
defaultCategory(1-24)apps/web/store/archive-sender-queue.ts (2)
addToArchiveSenderQueue(19-111)useArchiveSenderStatus(118-121)apps/web/components/Toast.tsx (1)
toastError(14-19)apps/web/components/bulk-archive/categoryIcons.ts (1)
getCategoryIcon(8-17)apps/web/components/EmailCell.tsx (1)
EmailCell(3-26)apps/web/hooks/useThreads.ts (1)
useThreads(8-28)apps/web/components/new-landing/icons/Link.tsx (1)
Link(1-18)apps/web/utils/date.ts (1)
formatShortDate(25-56)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (3)
apps/web/components/bulk-archive/categoryIcons.ts (1)
8-17: LGTM! Clean icon mapping implementation.The function correctly maps category names to appropriate lucide-react icons with case-insensitive matching and a sensible default fallback.
apps/web/__tests__/ai-categorize-senders.test.ts (1)
100-100: LGTM! Test correctly updated for category rename.The filter now excludes "Other" instead of "Unknown", properly aligning with the
UNKNOWN_CATEGORYconstant change.apps/web/utils/categories.ts (1)
1-24: No breaking change risk — existing categorizations are preserved.The categories are fully database-driven and user-scoped (linked to
emailAccountId). Existing senders with any category assignment will remain unchanged; theNewsletter.categoryIdfield supports any category stored in the database. ThebulkCategorizeSendersActionusesskipDuplicates: truewhen creating default categories, so it safely handles both new and existing users. No data migration is needed, and automation rules/filters are independent of thedefaultCategoryconstant since they reference actual database categories.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx:64">
P1: The `onComplete` callback is commented out and will never be called, even though the parent passes `handleSetupComplete`. This appears to be incomplete code - either uncomment the line and add `onComplete` to the dependency array, or remove the prop if it's not needed.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 401-428: The component BulkArchiveCards is manually handling
loading/error states with isLoading, error, and data?.threads; replace those
manual conditional blocks with the standardized LoadingContent component. Import
LoadingContent, pass isLoading and error (or map error to a message) and render
LoadingContent as the wrapper around the threads UI so that when isLoading or
error is true the LoadingContent handles display; after LoadingContent renders
its children, keep existing rendering for the empty state by checking
data?.threads.length and showing "No emails found" only when not loading and not
error. Ensure you update references to useThreads (fromEmail, limit, type) and
remove the manual JSX blocks that returned Skeleton or the error div.
- Around line 327-334: The inline double-cast bypasses type safety; remove "as
unknown as React.MouseEvent" and instead make the event parameter types align:
update the onClick handler to call onToggleSelection(e) without casting and
adjust the onToggleSelection prop signature in SenderRow (and its callers) to
accept the actual event type emitted by Checkbox (e.g.,
React.MouseEvent<HTMLElement> or the specific HTML element type Checkbox uses).
Ensure you import the correct React event type and update all implementations of
onToggleSelection to match.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/components/BulkArchiveCards.tsxapps/web/utils/actions/categorize.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveProgress.tsx
- apps/web/utils/actions/categorize.ts
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/BulkArchiveCards.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsx
🧠 Learnings (45)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*Form*.tsx : Handle form submission results using `result?.serverError` to show error toasts and `toastSuccess` to show success messages after server action completion
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : For data fetching, prefer using `useSWR` and follow the data-fetching guidelines
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If nested deeply in components, use `swr` to fetch via API instead of loading data directly
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : For data fetching in custom hooks, prefer using `useSWR` and wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) to wrap `useSWR` for individual API endpoints
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't reassign exceptions in catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-07-17T04:19:57.099Z
Learnt from: edulelis
Repo: elie222/inbox-zero PR: 576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses `[key: string]: DigestItem[] | undefined | string | Date | undefined` instead of intersection types like `& Record<string, DigestItem[] | undefined>` due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Do not export types/interfaces that are only used within the same file. Export later if needed
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary boolean casts
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : Always define types for A/B test variant flags (e.g., `type PricingVariant = "control" | "variant-a" | "variant-b"`) and provide type safety through type casting
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{ts,tsx} : Don't misuse the non-null assertion operator (!) in TypeScript files
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Accompany `onClick` with at least one of: `onKeyUp`, `onKeyDown`, or `onKeyPress`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.tsx : Use the `LoadingContent` component to handle loading states instead of manual loading state management
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use the `LoadingContent` component to handle loading states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Prefer functional components with hooks in React
Applied to files:
apps/web/components/BulkArchiveCards.tsx
🧬 Code graph analysis (2)
apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx (4)
apps/web/utils/email-account.ts (1)
checkUserOwnsEmailAccount(5-22)apps/web/utils/category.server.ts (1)
getUserCategoriesWithRules(24-39)apps/web/app/(app)/[emailAccountId]/PermissionsCheck.tsx (1)
PermissionsCheck(12-35)apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx (1)
BulkArchive(20-78)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (3)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/utils/actions/categorize.ts (1)
bulkCategorizeSendersAction(29-121)apps/web/components/SetupCard.tsx (1)
SetupCard(19-69)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (1)
apps/web/components/BulkArchiveCards.tsx (1)
32-35: ImportEmailGrouptype instead of duplicating it.The
EmailGrouptype is already defined in@/utils/bulk-archive/get-archive-candidates.ts. Import it rather than duplicating the definition to maintain consistency.♻️ Proposed fix
+import type { EmailGroup } from "@/utils/bulk-archive/get-archive-candidates"; import type { CategoryWithRules } from "@/utils/category.server"; import { useAccount } from "@/providers/EmailAccountProvider"; import { getCategoryIcon } from "@/components/bulk-archive/categoryIcons"; import { defaultCategory } from "@/utils/categories"; -type EmailGroup = { - address: string; - category: CategoryWithRules | null; -}; - export function BulkArchiveCards({⛔ Skipped due to learnings
Learnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-11-25T14:37:56.430Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpersLearnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/gmail-api.mdc:0-0 Timestamp: 2025-11-25T14:37:22.660Z Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API callsLearnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/gmail-api.mdc:0-0 Timestamp: 2025-11-25T14:37:22.660Z Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API callsLearnt from: edulelis Repo: elie222/inbox-zero PR: 576 File: packages/resend/emails/digest.tsx:78-83 Timestamp: 2025-07-17T04:19:57.099Z Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses `[key: string]: DigestItem[] | undefined | string | Date | undefined` instead of intersection types like `& Record<string, DigestItem[] | undefined>` due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.Learnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/gmail-api.mdc:0-0 Timestamp: 2025-11-25T14:37:22.660Z Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx:
- Around line 32-38: The SWR call in BulkArchive.tsx doesn't handle the error
state; destructure error from the useSWR call
(useSWR<CategorizedSendersResponse>(..., ) => const { data, error, mutate } =
useSWR(...)) and add a small effect that calls toastError(error) when error is
truthy (e.g., useEffect(() => { if (error) toastError(error); }, [error])); keep
existing refresh/fallback logic and ensure the toast import from
'@/components/Toast' is present and only invoked for real errors to provide user
feedback on failed API fetches.
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 32-35: Remove the duplicated EmailGroup type declaration in
BulkArchiveCards.tsx and import the existing EmailGroup type from the module
that defines it (the module that exports the original EmailGroup type), then
update the file to use the imported EmailGroup type wherever it’s referenced
(e.g., in component props/state). If the original module does not currently
export EmailGroup, add an exported type alias there first; otherwise simply add
an import for EmailGroup and delete the local type definition.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
🧠 Learnings (37)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't reassign exceptions in catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-07-17T04:19:57.099Z
Learnt from: edulelis
Repo: elie222/inbox-zero PR: 576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses `[key: string]: DigestItem[] | undefined | string | Date | undefined` instead of intersection types like `& Record<string, DigestItem[] | undefined>` due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Do not export types/interfaces that are only used within the same file. Export later if needed
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary boolean casts
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Accompany `onClick` with at least one of: `onKeyUp`, `onKeyDown`, or `onKeyPress`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : Always define types for A/B test variant flags (e.g., `type PricingVariant = "control" | "variant-a" | "variant-b"`) and provide type safety through type casting
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.tsx : Use the `LoadingContent` component to handle loading states instead of manual loading state management
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use the `LoadingContent` component to handle loading states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Prefer functional components with hooks in React
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If nested deeply in components, use `swr` to fetch via API instead of loading data directly
Applied to files:
apps/web/components/BulkArchiveCards.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*Form*.tsx : Handle form submission results using `result?.serverError` to show error toasts and `toastSuccess` to show success messages after server action completion
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : For data fetching, prefer using `useSWR` and follow the data-fetching guidelines
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : For data fetching in custom hooks, prefer using `useSWR` and wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) to wrap `useSWR` for individual API endpoints
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: test
apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @apps/web/components/BulkArchiveCards.tsx:
- Around line 407-429: The component currently uses manual conditional rendering
for loading, error, and empty states (checking isLoading, error, and
!data?.threads.length) which should be replaced with the shared LoadingContent
component for consistent UI; update BulkArchiveCards to wrap or replace the
existing if-blocks so that when isLoading or error are true you render
LoadingContent (passing appropriate props/message or children) and handle the
empty state via LoadingContent or its designated empty-state slot, keeping the
data rendering logic for threads unchanged and referencing the same isLoading,
error, and data?.threads.length checks to drive LoadingContent.
- Around line 32-35: Replace the local duplicated EmailGroup type in
BulkArchiveCards.tsx with an import from the existing definition in
@/utils/bulk-archive/get-archive-candidates.ts; update the top-level imports to
include { EmailGroup } (or the exported name used there) and remove the local
type declaration so the component uses the single source of truth for
EmailGroup.
🧹 Nitpick comments (2)
apps/web/components/BulkArchiveCards.tsx (2)
154-159: Consider parallel execution for better performance.The sequential
awaitin the loop archives senders one at a time, which could be slow for many senders. Consider usingPromise.all()to archive in parallel, unless this sequential approach is intentional for rate-limiting purposes.♻️ Proposed parallel implementation
try { - for (const sender of selectedToArchive) { - await addToArchiveSenderQueue({ + await Promise.all( + selectedToArchive.map((sender) => + addToArchiveSenderQueue({ - sender: sender.address, - emailAccountId, - }); - } + sender: sender.address, + emailAccountId, + }), + ), + ); setArchivedCategories((prev) => ({ ...prev, [categoryName]: true }));
462-472: Consider extracting snippet cleaning logic to a helper function.The inline IIFE for cleaning the snippet is complex and could be extracted to a named helper function for better readability and reusability.
♻️ Proposed helper function
Add to the bottom of the file:
function cleanSnippet(snippet: string, maxLength: number = 80): string { // Remove invisible/zero-width chars and normalize whitespace const cleaned = snippet .replace(/[\u034F\u200B-\u200D\uFEFF\u00A0]/g, "") .trim() .replace(/\s+/g, " "); return cleaned.length > maxLength ? `${cleaned.slice(0, maxLength).trimEnd()}...` : cleaned; }Then use it:
{snippet && ( <span className="ml-2 text-muted-foreground"> - {(() => { - // Remove invisible/zero-width chars and normalize whitespace - const cleaned = snippet - .replace(/[\u034F\u200B-\u200D\uFEFF\u00A0]/g, "") - .trim() - .replace(/\s+/g, " "); - return cleaned.length > 80 - ? `${cleaned.slice(0, 80).trimEnd()}...` - : cleaned; - })()} + {cleanSnippet(snippet)} </span> )}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/components/BulkArchiveCards.tsx
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/BulkArchiveCards.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/components/BulkArchiveCards.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/BulkArchiveCards.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/components/BulkArchiveCards.tsx
🧠 Learnings (28)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make static elements with click handlers use a valid role attribute
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `target="_blank"` without `rel="noopener"`
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure all anchors are valid and navigable
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't reassign exceptions in catch clauses
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary boolean casts
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : Always define types for A/B test variant flags (e.g., `type PricingVariant = "control" | "variant-a" | "variant-b"`) and provide type safety through type casting
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.tsx : Use the `LoadingContent` component to handle loading states instead of manual loading state management
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use the `LoadingContent` component to handle loading states
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Prefer functional components with hooks in React
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If nested deeply in components, use `swr` to fetch via API instead of loading data directly
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2025-07-17T04:19:57.099Z
Learnt from: edulelis
Repo: elie222/inbox-zero PR: 576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses `[key: string]: DigestItem[] | undefined | string | Date | undefined` instead of intersection types like `& Record<string, DigestItem[] | undefined>` due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.
Applied to files:
apps/web/components/BulkArchiveCards.tsx
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/components/BulkArchiveCards.tsx
…ications category
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/components/PageHeader.tsx (1)
29-36: Fix layout bug: description renders horizontally instead of below title.The
descriptionis currently inside theflex items-center gap-2container (line 30), causing it to render horizontally alongside the title andrightElement. A subtitle (PageSubHeading) should appear below the title (PageHeading), not beside it.🔧 Proposed fix
Move the description outside the inner flex container:
<div className="flex flex-col sm:flex-row items-start sm:items-center mt-1 gap-3"> - <div className="flex items-center gap-2"> + <div> + <div className="flex items-center gap-2"> <PageHeading>{title}</PageHeading> {rightElement} - {description && ( - <PageSubHeading className="mt-1">{description}</PageSubHeading> - )} + </div> + {description && ( + <PageSubHeading className="mt-1">{description}</PageSubHeading> + )} </div>apps/web/utils/categories.ts (1)
1-33: Fix test expectations for non-existent categories.The test file
apps/web/__tests__/ai-categorize-senders.test.tshas hardcoded expectations for categories that no longer exist indefaultCategory. Lines 26-48 definetestSenderswithexpectedCategoryvalues of "Support" and "Social", but these categories were removed from the new taxonomy. This will cause test failures:
- Line 31:
expectedCategory: "Support"— does not exist (removed)- Line 48:
expectedCategory: "Social"— does not exist (removed)The assertions at lines 75 and 81 (
expect(supportResult?.category).toBe("Support")and similar) will fail because these categories are not ingetEnabledCategories().Update the test data to expect only categories that exist in the current
defaultCategory: OTHER, NEWSLETTER, MARKETING, RECEIPT, or NOTIFICATION.
🤖 Fix all issues with AI agents
In
@apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx:
- Line 4: Replace the direct sonner import and usages: remove "import { toast }
from 'sonner'" and instead import the wrapper functions "toastSuccess" and
"toastError" from "@/components/Toast"; then update all calls using
toast.success(...) to toastSuccess(...) and toast.error(...) to toastError(...),
including any occurrences inside the AutoCategorizationSetup server action
handlers or helper functions so server-action error/success handling uses the
project's toast wrappers.
🧹 Nitpick comments (1)
apps/web/components/bulk-archive/categoryIcons.ts (1)
9-19: Consider adding explicit "uncategorized" case for consistency.The
getCategoryStylefunction (line 56) has an explicit check for "uncategorized", whilegetCategoryIconrelies on the default case. Although both returnMailIcon, adding an explicit case here would improve consistency and make the special handling of "uncategorized" clearer throughout the codebase.♻️ Suggested enhancement
export function getCategoryIcon(categoryName: string) { const name = categoryName.toLowerCase(); if (name.includes("newsletter")) return NewspaperIcon; if (name.includes("marketing")) return MegaphoneIcon; if (name.includes("receipt")) return ReceiptIcon; if (name.includes("notification")) return BellIcon; + if (name === "uncategorized") return MailIcon; // Default icon for "Other" and any other category return MailIcon; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsxapps/web/components/BulkArchiveCards.tsxapps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.tsapps/web/utils/redis/categorization-progress.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/components/BulkArchiveCards.tsx
- apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchive.tsx
🧰 Additional context used
📓 Path-based instructions (22)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/PageHeader.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/PageHeader.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/PageHeader.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/PageHeader.tsxapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/components/PageHeader.tsxapps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/utils/categories.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/components/bulk-archive/categoryIcons.tsapps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/redis/categorization-progress.tsapps/web/utils/ai/categorize-sender/ai-categorize-senders.tsapps/web/utils/categories.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
LLM-related code must be organized in specific directories:
apps/web/utils/ai/for main implementations,apps/web/utils/llms/for core utilities and configurations, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
apps/web/utils/ai/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.ts: LLM feature functions must import fromzodfor schema validation, usecreateScopedLoggerfrom@/utils/logger,chatCompletionObjectandcreateGenerateObjectfrom@/utils/llms, and importEmailAccountWithAItype from@/utils/llms/types
LLM feature functions must follow a standard structure: accept options withinputDataandemailAccountparameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and usecreateGenerateObjectto execute the LLM call
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures usingwithRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
🧠 Learnings (7)
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : User prompts must contain the actual data and context, and should be kept separate from system prompts
Applied to files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : System prompts must define the LLM's role and task specifications
Applied to files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Applied to files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must follow a standard structure: accept options with `inputData` and `emailAccount` parameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and use `createGenerateObject` to execute the LLM call
Applied to files:
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (3)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/utils/actions/categorize.ts (1)
bulkCategorizeSendersAction(29-121)apps/web/components/SetupCard.tsx (1)
SetupCard(19-69)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
🔇 Additional comments (9)
apps/web/components/PageHeader.tsx (2)
14-19: LGTM!The new optional
rightElementprop is correctly typed asReact.ReactNodeand follows React best practices.
21-26: LGTM!The function signature correctly destructures the new
rightElementprop alongside existing props.apps/web/utils/redis/categorization-progress.ts (1)
65-72: LGTM!The implementation is clean and consistent with the existing functions in the file. It properly uses the shared
getKeyhelper and follows the same parameter destructuring pattern.apps/web/components/bulk-archive/categoryIcons.ts (2)
1-7: LGTM!The imports are specific and all used within the file, following the guideline to minimize bundle size.
21-71: All custom Tailwind color classes are properly defined in the configuration.The colors used in
getCategoryStyle()(new-purple, new-orange, new-green, new-pink, new-indigo, new-blue with shades 50, 100, 150, 200, 300, 500, 600) are all defined inapps/web/tailwind.config.jsunder theextend.colors.newobject. The function will render correctly without styling issues.apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (4)
12-29: LGTM!The features array is well-structured with clear, user-friendly descriptions that effectively communicate the value proposition of the bulk archive feature.
31-35: LGTM!Component initialization and hook usage follow React best practices.
37-64: Polling issue has been resolved.The previous concern about indefinite polling when no senders need categorization has been properly addressed. The
elseblock (lines 52-55) correctly callssetIsBulkCategorizing(false)when there are no uncategorized senders, preventing unnecessary polling.The overall error handling logic is sound:
- Success with senders → continues polling for progress updates
- Success without senders → stops polling immediately
- Error → stops polling and shows user feedback
66-78: LGTM!The component rendering uses
SetupCardappropriately with all required props, and the button correctly reflects the loading state for good UX.
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/components/PageHeader.tsx">
<violation number="1" location="apps/web/components/PageHeader.tsx:30">
P2: Layout bug: `flex items-center` causes `PageSubHeading` (description) to appear horizontally next to the title instead of below it. The flex container should only wrap `PageHeading` and `rightElement`, with the description outside.</violation>
</file>
<file name="apps/web/components/bulk-archive/categoryIcons.ts">
<violation number="1" location="apps/web/components/bulk-archive/categoryIcons.ts:56">
P2: Inconsistent pattern matching: `uncategorized` uses strict equality (`===`) while all other categories use `.includes()`. Category names like "Uncategorized Emails" won't match. Consider using `name.includes("uncategorized")` for consistency.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (1)
37-64: Simplify error handling by removing unnecessary throw/catch.The current pattern throws an error on Line 45 just to immediately catch it on Line 56. Per the project's preferred patterns for server action error handling, you can handle
serverErrordirectly without the throw/catch:♻️ Proposed simplification
const enableFeature = useCallback(async () => { setIsEnabling(true); setIsBulkCategorizing(true); try { const result = await bulkCategorizeSendersAction(emailAccountId); if (result?.serverError) { - throw new Error(result.serverError); + toastError({ description: result.serverError }); + setIsBulkCategorizing(false); + return; } if (result?.data?.totalUncategorizedSenders) { toastSuccess({ description: `Categorizing ${result.data.totalUncategorizedSenders} senders... This may take a few minutes.`, }); } else { toastSuccess({ description: "No uncategorized senders found." }); setIsBulkCategorizing(false); } } catch (error) { - toastError({ - description: `Failed to enable feature: ${error instanceof Error ? error.message : "Unknown error"}`, - }); - setIsBulkCategorizing(false); + // Handle unexpected errors + toastError({ + description: `Failed to enable feature: ${error instanceof Error ? error.message : "Unknown error"}`, + }); + setIsBulkCategorizing(false); } finally { setIsEnabling(false); } }, [emailAccountId, setIsBulkCategorizing]);Based on learnings, prefer direct error handling with
result?.serverErrorchecks rather than throw/catch patterns for server actions.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/PageHeader.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/PageHeader.tsx
🧠 Learnings (14)
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*Form*.tsx : Handle form submission results using `result?.serverError` to show error toasts and `toastSuccess` to show success messages after server action completion
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the `next-safe-action` library with actionClient, actionClientUser, or adminActionClient for type safety and validation
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx
📚 Learning: 2026-01-07T21:07:06.691Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: In TSX files across the codebase, prefer direct server action calls with manual error handling (e.g., check result?.serverError and handle errors explicitly). The useAction hook pattern is not required; you can call server actions directly and use try/finally to ensure cleanup (such as resetting loading states) regardless of the hook. Apply consistently for components that perform server interactions.
Applied to files:
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsxapps/web/components/PageHeader.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (4)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(79-89)apps/web/utils/actions/categorize.ts (1)
bulkCategorizeSendersAction(29-121)apps/web/components/Toast.tsx (2)
toastSuccess(3-12)toastError(14-19)apps/web/components/SetupCard.tsx (1)
SetupCard(19-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (2)
apps/web/components/PageHeader.tsx (1)
18-18: LGTM! Clean implementation of the optional right element.The addition of the
rightElementprop is well-implemented and follows React best practices. The flex layout withitems-center gap-2appropriately positions the element next to the title, and the optional typing ensures backward compatibility with existing usage.Also applies to: 21-34
apps/web/app/(app)/[emailAccountId]/bulk-archive/AutoCategorizationSetup.tsx (1)
42-42: No changes needed. The action is correctly called withemailAccountIdas an argument, which aligns with the project's pattern of bindingemailAccountIdwhen calling actions from the client. This is consistent with how the action is used elsewhere in the codebase (CategorizeWithAiButton.tsx).
Add Bulk Archive and Quick Bulk Archive flows with account-scoped redirects and in-page categorization progress in apps/web
Introduce account-scoped routes for bulk archive and quick bulk archive, add client pages for categorization progress and queueing archives, provide a provider-agnostic categorization batch handler, and expose a categorized senders API. Core entry points are implemented in apps/web/app/(app)/(redirects)/bulk-archive/page.tsx, apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx, apps/web/app/(app)/[emailAccountId]/quick-bulk-archive/page.tsx, and apps/web/app/api/user/categorize/senders/categorized/route.ts.
📍Where to Start
Start with the server page that wires data and permissions for Bulk Archive in apps/web/app/(app)/[emailAccountId]/bulk-archive/page.tsx, then follow the flow into
BulkArchiveContentin apps/web/app/(app)/[emailAccountId]/bulk-archive/BulkArchiveContent.tsx and the progress/polling logic.Macroscope summarized 823d56f.
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.
Note
Enables bulk archiving by sender categories with real-time categorization status and streamlined data fetching.
bulk-archiveandquick-bulk-archivewith UI: category cards, per-sender previews, selection, and archiving queueingBulkArchiveProgress+ProgressPanel; SWR polling andAutoCategorizationSetupto kick off AI categorizationGET /api/user/categorize/senders/categorizedendpoint for refreshed datacreateEmailProvider(provider-agnostic), removing Gmail-specific client/token usagebulkCategorizeSendersActionnow upserts default categories before queuing workEmailCellsubtitle visibility,ProgressPanelmarkup)Written by Cursor Bugbot for commit 823d56f. Configure here.