-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add org analytics view #1082
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
Add org analytics view #1082
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds organization statistics: UI components and pages (OrganizationTabs, OrgStats, stats page), authenticated API endpoints (totals, email-buckets, rules-buckets) with types and Zod params, SWR hooks for those endpoints and organization data, an organization GET route with admin check, a small InviteMember button size tweak, and a version bump. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser as Client (React)
participant API as Next.js API Routes
participant DB as Database
User->>Browser: Navigate to /organization/:id/stats
Browser->>API: GET /api/organizations/:id
API->>DB: SELECT organization (id, name)
DB-->>API: organization
API-->>Browser: organization JSON
Browser->>API: GET /api/organizations/:id/stats/totals?fromDate&toDate
Browser->>API: GET /api/organizations/:id/stats/email-buckets?fromDate&toDate
Browser->>API: GET /api/organizations/:id/stats/rules-buckets?fromDate&toDate
API->>DB: aggregate totals query
API->>DB: per-member email counts query
API->>DB: per-member executed-rules query
DB-->>API: aggregated results
API->>API: bucket and format results
API-->>Browser: JSON (totals, emailBuckets, rulesBuckets)
Browser->>Browser: Render StatCards & BucketCharts using hooks
Browser-->>User: Display statistics UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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.
No issues found across 8 files
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: 7
🧹 Nitpick comments (2)
apps/web/hooks/useOrgStatsTotals.ts (1)
9-16: Extract duplicated query string building logic.The query string construction logic (lines 9-16) is duplicated across three hooks:
useOrgStatsTotals,useOrgStatsEmailBuckets, anduseOrgStatsRulesBuckets. This violates the DRY principle.Create a shared helper function to build the query string from
OrgStatsParams:// In a new file: apps/web/utils/organizationStats.ts import type { OrgStatsParams } from "@/app/api/organizations/[organizationId]/stats/types"; export function buildOrgStatsQueryString(options?: OrgStatsParams): string { const params = new URLSearchParams(); if (options?.fromDate) { params.set("fromDate", options.fromDate.toString()); } if (options?.toDate) { params.set("toDate", options.toDate.toString()); } const queryString = params.toString(); return queryString ? `?${queryString}` : ""; }Then update this hook and the other two hooks to use it:
import useSWR from "swr"; import type { OrgTotalsResponse } from "@/app/api/organizations/[organizationId]/stats/totals/route"; import type { OrgStatsParams } from "@/app/api/organizations/[organizationId]/stats/types"; +import { buildOrgStatsQueryString } from "@/utils/organizationStats"; export function useOrgStatsTotals( organizationId: string, options?: OrgStatsParams, ) { - const params = new URLSearchParams(); - if (options?.fromDate) { - params.set("fromDate", options.fromDate.toString()); - } - if (options?.toDate) { - params.set("toDate", options.toDate.toString()); - } - const queryString = params.toString(); + const queryString = buildOrgStatsQueryString(options); return useSWR<OrgTotalsResponse>( - `/api/organizations/${organizationId}/stats/totals${queryString ? `?${queryString}` : ""}`, + `/api/organizations/${organizationId}/stats/totals${queryString}`, ); }Apply the same change to
useOrgStatsEmailBuckets.tsanduseOrgStatsRulesBuckets.ts.apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx (1)
172-229: Consider adding ARIA attributes for better accessibility.The progress bar visualization would benefit from accessibility attributes to convey meaning to screen readers.
- <div className="h-2 w-full rounded-full bg-secondary"> - <div - className="h-2 rounded-full bg-primary transition-all" - style={{ - width: `${(bucket.userCount / maxValue) * 100}%`, - }} - /> - </div> + <div + className="h-2 w-full rounded-full bg-secondary" + role="progressbar" + aria-valuenow={bucket.userCount} + aria-valuemin={0} + aria-valuemax={maxValue} + aria-label={`${bucket.label} ${unit}: ${bucket.userCount} users`} + > + <div + className="h-2 rounded-full bg-primary transition-all" + style={{ + width: `${(bucket.userCount / maxValue) * 100}%`, + }} + /> + </div>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
apps/web/app/(app)/organization/[organizationId]/Members.tsx(1 hunks)apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx(1 hunks)apps/web/app/(app)/organization/[organizationId]/page.tsx(2 hunks)apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx(1 hunks)apps/web/app/(app)/organization/[organizationId]/stats/page.tsx(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/types.ts(1 hunks)apps/web/components/InviteMemberModal.tsx(1 hunks)apps/web/hooks/useOrgStatsEmailBuckets.ts(1 hunks)apps/web/hooks/useOrgStatsRulesBuckets.ts(1 hunks)apps/web/hooks/useOrgStatsTotals.ts(1 hunks)version.txt(1 hunks)
🧰 Additional context used
📓 Path-based instructions (25)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use@/path aliases for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Follow consistent naming conventions using PascalCase for components
Centralize shared types in dedicated type filesImport specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
Files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
apps/web/hooks/use*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Use SWR for client-side data fetching with typed responses from GET API routes
apps/web/hooks/use*.ts: Use SWR hooks for client-side data fetching, with hooks stored inapps/web/hooks/use*.tsthat return typed responses from GET API routes
Callmutate()on SWR hooks after successful mutations to refresh cached data
apps/web/hooks/use*.ts: Use theuseprefix for custom hook filenames (e.g.,useAccounts.ts)
For data fetching in custom hooks, prefer usinguseSWRand wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially themutatefunction
Create dedicated hooks for specific data types (e.g.,useAccounts,useLabels) to wrapuseSWRfor individual API endpoints
Files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
**/*.{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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
apps/web/hooks/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/hooks.mdc)
Place custom hooks in the
apps/web/hooks/directory
Files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxversion.txtapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.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/hooks/useOrgStatsEmailBuckets.tsapps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/hooks/useOrgStatsTotals.tsapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
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)/organization/[organizationId]/Members.tsxapps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/(app)/organization/[organizationId]/stats/page.tsxapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks over class components
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Use LoadingContent component for async data with loading and error states
Files:
apps/web/app/(app)/organization/[organizationId]/Members.tsxapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/Members.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/Members.tsxapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/Members.tsxapps/web/components/InviteMemberModal.tsxapps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsxapps/web/app/(app)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsxapps/web/app/(app)/organization/[organizationId]/stats/page.tsx
apps/web/app/api/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/*.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware for authentication
Export response types from GET API routes usingAwaited<ReturnType<>>pattern for type-safe client usage
Files:
apps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
**/{server,api,actions,utils}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{server,api,actions,utils}/**/*.ts: UsecreateScopedLoggerfrom "@/utils/logger" for logging in backend code
Add thecreateScopedLoggerinstantiation at the top of the file with an appropriate scope name
Use.with()method to attach context variables only within specific functions, not on global loggers
For large functions with reused variables, usecreateScopedLogger().with()to attach context once and reuse the logger without passing variables repeatedly
Files:
apps/web/app/api/organizations/[organizationId]/stats/types.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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/organizations/[organizationId]/stats/types.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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/organizations/[organizationId]/stats/types.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/components/**/*.tsx: Use React Hook Form with Zod validation for form components
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in form submissionsUse
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/InviteMemberModal.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/InviteMemberModal.tsx
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/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
🧠 Learnings (40)
📚 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/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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/hooks/use*.ts : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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 : Use the `use` prefix for custom hook filenames (e.g., `useAccounts.ts`)
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.ts
📚 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: Applies to apps/web/hooks/useFeatureFlags.ts : All feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts`
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.ts
📚 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/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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 : Custom hooks must use the `use` prefix in their filename (e.g., `useAccounts.ts`)
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/hooks/use*.ts : Use SWR for client-side data fetching with typed responses from GET API routes
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.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} : For API get requests to server, use the `swr` package with `useSWR` hook
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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 API GET requests to server, use the `swr` package with hooks like `useSWR` to fetch data
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.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: Use `swr` for data fetching in deeply nested components
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.ts
📚 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} : For API GET requests to server, use the `swr` package
Applied to files:
apps/web/hooks/useOrgStatsEmailBuckets.tsapps/web/hooks/useOrgStatsRulesBuckets.tsapps/web/hooks/useOrgStatsTotals.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)/organization/[organizationId]/Members.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/utils/actions/*.validation.ts : Export types from Zod schemas using `z.infer<>` to maintain type safety between validation and client usage
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/utils/actions/**/*.validation.ts : Use Zod schemas for validation and export both schema and inferred types in validation files
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.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/app/api/organizations/[organizationId]/stats/types.ts
📚 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/*.validation.ts : Define input validation schemas using Zod in `.validation.ts` files and export both the schema and its inferred TypeScript type
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.ts
📚 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/utils/actions/*.validation.ts : Define Zod validation schemas in separate `*.validation.ts` files and export both the schema and inferred type (e.g., `CreateExampleBody`)
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.ts
📚 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} : Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.ts
📚 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 : Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/types.ts
📚 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)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/stats/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)/organization/[organizationId]/page.tsxapps/web/app/(app)/organization/[organizationId]/stats/page.tsx
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Follow consistent naming conventions using PascalCase for components
Applied to files:
apps/web/app/(app)/organization/[organizationId]/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/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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 : Use Prisma for database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/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 : Use Prisma for all database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All related resources in database queries must use proper relationship filtering (not just top-level user/account filtering)
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/app/api/**/*.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware for authentication
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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)/organization/[organizationId]/stats/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)/organization/[organizationId]/stats/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 : Do NOT use POST API routes for mutations - always use server actions with `next-safe-action` instead
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.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/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/api/organizations/[organizationId]/stats/rules-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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/(ai/digest|resend/digest|clean/gmail|user/categorize/senders/batch)/**/*.{ts,tsx} : QStash endpoints must use `verifySignatureAppRouter` middleware to verify request signatures and prevent request spoofing
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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 : ALL API routes that handle user data MUST use appropriate middleware: use `withEmailAccount` for email-scoped operations, use `withAuth` for user-scoped operations, or use `withError` with proper validation for public/custom auth endpoints
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
🧬 Code graph analysis (7)
apps/web/hooks/useOrgStatsEmailBuckets.ts (2)
apps/web/app/api/organizations/[organizationId]/stats/types.ts (1)
OrgStatsParams(7-7)apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts (1)
OrgEmailBucketsResponse(16-18)
apps/web/hooks/useOrgStatsRulesBuckets.ts (2)
apps/web/app/api/organizations/[organizationId]/stats/types.ts (1)
OrgStatsParams(7-7)apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts (1)
OrgRulesBucketsResponse(16-18)
apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx (5)
apps/web/hooks/useOrgStatsTotals.ts (1)
useOrgStatsTotals(5-21)apps/web/hooks/useOrgStatsEmailBuckets.ts (1)
useOrgStatsEmailBuckets(5-21)apps/web/hooks/useOrgStatsRulesBuckets.ts (1)
useOrgStatsRulesBuckets(5-21)apps/web/components/DatePickerWithRange.tsx (1)
DatePickerWithRange(37-115)apps/web/components/new-landing/common/Card.tsx (3)
Card(67-105)CardHeader(22-54)CardContent(10-12)
apps/web/hooks/useOrgStatsTotals.ts (2)
apps/web/app/api/organizations/[organizationId]/stats/types.ts (1)
OrgStatsParams(7-7)apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts (1)
OrgTotalsResponse(8-8)
apps/web/app/(app)/organization/[organizationId]/page.tsx (2)
apps/web/components/PageWrapper.tsx (1)
PageWrapper(3-20)apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx (1)
OrganizationTabs(12-44)
apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx (1)
apps/web/components/TabSelect.tsx (1)
TabSelect(44-103)
apps/web/app/(app)/organization/[organizationId]/stats/page.tsx (3)
apps/web/components/PageWrapper.tsx (1)
PageWrapper(3-20)apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx (1)
OrganizationTabs(12-44)apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx (1)
OrgStats(23-148)
⏰ 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: test
- GitHub Check: Review for correctness
🔇 Additional comments (16)
version.txt (1)
1-1: Version bump approved.The patch version increment (v2.21.58 → v2.21.59) is correctly formatted. Note that introducing new analytics functionality typically warrants a minor version bump (e.g., v2.22.0) rather than a patch release, but this may align with your versioning strategy.
Please confirm that this patch-level bump is intentional for a feature addition, or consider whether a minor version bump would be more appropriate.
apps/web/components/InviteMemberModal.tsx (1)
84-84: LGTM! Button sizing improves visual consistency.The addition of
size="sm"is appropriate for a secondary action button, especially alongside the new analytics navigation UI introduced in this PR.apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts (3)
1-6: LGTM!Imports follow project conventions: uses
@/path aliases, imports Prisma from the correct location (@/generated/prisma/client), and uses the centralized prisma utility.
20-42: LGTM!The GET handler correctly uses
withAuthmiddleware, validates admin access, parses query params with Zod schema, and returns JSON response. Follows project conventions for API routes.
44-94: LGTM!The
getEmailVolumeBucketsfunction properly:
- Uses parameterized raw SQL to prevent injection
- Scopes data to organization via Member join
- Handles bigint conversion safely
- Buckets results correctly with early break
The date filtering and SQL composition are clean and consistent with the other stats routes.
apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts (3)
1-8: LGTM!Imports and type export follow project conventions. The
OrgTotalsResponsetype is correctly exported using theAwaited<ReturnType<>>pattern for client-side type safety.
10-32: LGTM!The GET handler properly implements authentication with
withAuth, validates admin access, parses date params, and returns a JSON response. Consistent with other stats routes in this PR.
34-99: LGTM!The
getTotalsfunction efficiently combines three aggregations into a single raw SQL query. The date filtering logic is correctly separated for emails (usingem.date) and rules (usinger."createdAt"), andactive_membersappropriately counts all members regardless of date range. Bigint-to-number conversion with fallback is handled safely.apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts (3)
1-6: LGTM!Imports are consistent with the other stats routes in this PR.
20-42: LGTM!GET handler follows the same secure pattern as the other stats routes:
withAuthmiddleware, admin access validation, Zod-parsed query params, and JSON response.
44-94: LGTM!The
getExecutedRulesBucketsfunction correctly builds parameterized SQL with date filtering, joins through Member for organization scoping, and buckets results in JavaScript. Implementation is consistent with email-buckets.apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx (5)
1-13: LGTM!Client component correctly marked with
"use client". Imports use@/path aliases and import specific date-fns functions to minimize bundle size.
15-47: LGTM!Date state management is well-structured with
useMemoanduseCallbackfor performance. Theoptionsobject correctly converts dates to timestamps for the API hooks.
49-66: LGTM!All three data-fetching hooks are correctly wired with
organizationIdandoptions. The destructuredisLoadinganderrorstates integrate well withLoadingContentbelow.
67-147: LGTM!The JSX structure properly uses
LoadingContentwrappers for each data section with appropriate skeleton placeholders. The grid layout is responsive withmd:grid-cols-*breakpoints.
150-170: LGTM!
StatCardis a clean, focused helper component. Uses shadcn/ui Card components consistently.
| ]; | ||
|
|
||
| // Determine selected tab based on pathname | ||
| const selected = pathname.includes("/stats") ? "stats" : "members"; |
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.
Use more precise pathname matching.
Using pathname.includes("/stats") is fragile because it will match any occurrence of "stats" in the pathname, potentially causing incorrect tab selection if "stats" appears in a query parameter or elsewhere in the URL.
Use a more precise check:
- const selected = pathname.includes("/stats") ? "stats" : "members";
+ const selected = pathname.endsWith("/stats") ? "stats" : "members";Or for even more precision:
- const selected = pathname.includes("/stats") ? "stats" : "members";
+ const selected = pathname === `/organization/${organizationId}/stats` ? "stats" : "members";🤖 Prompt for AI Agents
In apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx around
line 32, the current selected-tab logic uses pathname.includes("/stats") which
is too broad; change it to compare the pathname path portion exactly (e.g.,
strip query/hash and check endsWith or exact segment match) so only the actual
stats route selects the stats tab — for example, derive the raw path (remove
search/hash) and use path.endsWith("/stats") or split the path into segments and
compare the last segment === "stats".
apps/web/app/(app)/organization/[organizationId]/stats/page.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
Show resolved
Hide resolved
apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts
Outdated
Show resolved
Hide resolved
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
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/api/organizations/[organizationId]/route.ts (1)
6-6: LGTM! Type export follows the correct pattern.The type inference using
Awaited<ReturnType<>>is correct as per guidelines.Optional naming convention nitpick: The coding guidelines suggest naming GET route response types as
Get[Feature]Response(e.g.,GetOrganizationResponse), thoughOrganizationResponseis also clear and acceptable.apps/web/hooks/useOrganization.ts (1)
4-6: Consider conditional fetching and explicit return properties.The hook implementation is correct, but could be enhanced for better usability:
1. Add conditional fetching to prevent unnecessary API calls when
organizationIdis empty or undefined:export function useOrganization(organizationId: string) { - return useSWR<OrganizationResponse>(`/api/organizations/${organizationId}`); + return useSWR<OrganizationResponse>( + organizationId ? `/api/organizations/${organizationId}` : null + ); }2. Return destructured properties for better ergonomics, aligning with coding guidelines that suggest hooks should return data, loading state, error state, and the mutate function:
export function useOrganization(organizationId: string) { - return useSWR<OrganizationResponse>(`/api/organizations/${organizationId}`); + const { data, error, isLoading, mutate } = useSWR<OrganizationResponse>( + organizationId ? `/api/organizations/${organizationId}` : null + ); + + return { + organization: data, + isLoading, + error, + mutate, + }; }Based on learnings, custom hooks for data fetching should wrap
useSWRand return data, loading state, error state, and themutatefunction.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx(1 hunks)apps/web/app/(app)/organization/[organizationId]/page.tsx(2 hunks)apps/web/app/(app)/organization/[organizationId]/stats/page.tsx(1 hunks)apps/web/app/api/organizations/[organizationId]/route.ts(1 hunks)apps/web/hooks/useOrganization.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/app/(app)/organization/[organizationId]/stats/page.tsx
- apps/web/app/(app)/organization/[organizationId]/OrganizationTabs.tsx
- apps/web/app/(app)/organization/[organizationId]/page.tsx
🧰 Additional context used
📓 Path-based instructions (19)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use@/path aliases for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Follow consistent naming conventions using PascalCase for components
Centralize shared types in dedicated type filesImport specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
Files:
apps/web/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.ts
apps/web/hooks/use*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Use SWR for client-side data fetching with typed responses from GET API routes
apps/web/hooks/use*.ts: Use SWR hooks for client-side data fetching, with hooks stored inapps/web/hooks/use*.tsthat return typed responses from GET API routes
Callmutate()on SWR hooks after successful mutations to refresh cached data
apps/web/hooks/use*.ts: Use theuseprefix for custom hook filenames (e.g.,useAccounts.ts)
For data fetching in custom hooks, prefer usinguseSWRand wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially themutatefunction
Create dedicated hooks for specific data types (e.g.,useAccounts,useLabels) to wrapuseSWRfor individual API endpoints
Files:
apps/web/hooks/useOrganization.ts
**/*.{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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.ts
apps/web/hooks/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/hooks.mdc)
Place custom hooks in the
apps/web/hooks/directory
Files:
apps/web/hooks/useOrganization.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.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/hooks/useOrganization.tsapps/web/app/api/organizations/[organizationId]/route.ts
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/organizations/[organizationId]/route.ts
apps/web/app/api/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/*.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware for authentication
Export response types from GET API routes usingAwaited<ReturnType<>>pattern for type-safe client usage
Files:
apps/web/app/api/organizations/[organizationId]/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/organizations/[organizationId]/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/organizations/[organizationId]/route.ts
**/{server,api,actions,utils}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{server,api,actions,utils}/**/*.ts: UsecreateScopedLoggerfrom "@/utils/logger" for logging in backend code
Add thecreateScopedLoggerinstantiation at the top of the file with an appropriate scope name
Use.with()method to attach context variables only within specific functions, not on global loggers
For large functions with reused variables, usecreateScopedLogger().with()to attach context once and reuse the logger without passing variables repeatedly
Files:
apps/web/app/api/organizations/[organizationId]/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/organizations/[organizationId]/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/organizations/[organizationId]/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/organizations/[organizationId]/route.ts
🧠 Learnings (21)
📚 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/hooks/useOrganization.ts
📚 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/hooks/use*.ts : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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/hooks/useOrganization.ts
📚 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/hooks/useOrganization.ts
📚 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/hooks/useOrganization.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/hooks/use*.ts : Use SWR for client-side data fetching with typed responses from GET API routes
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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 API GET requests to server, use the `swr` package with hooks like `useSWR` to fetch data
Applied to files:
apps/web/hooks/useOrganization.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} : For API get requests to server, use the `swr` package with `useSWR` hook
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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 : Use the `use` prefix for custom hook filenames (e.g., `useAccounts.ts`)
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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: Applies to apps/web/hooks/useFeatureFlags.ts : All feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts`
Applied to files:
apps/web/hooks/useOrganization.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: Use `swr` for data fetching in deeply nested components
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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} : For API GET requests to server, use the `swr` package
Applied to files:
apps/web/hooks/useOrganization.ts
📚 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/organizations/[organizationId]/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/organizations/[organizationId]/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/organizations/[organizationId]/route.ts
📚 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 : Always export response types from GET routes as `Get[Feature]Response` using type inference from the data fetching function for type-safe client consumption
Applied to files:
apps/web/app/api/organizations/[organizationId]/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 : Use Prisma for database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/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/organizations/[organizationId]/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 : Use Prisma for all database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/route.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/app/api/**/*.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware for authentication
Applied to files:
apps/web/app/api/organizations/[organizationId]/route.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/app/api/**/*.ts : Export response types from GET API routes using `Awaited<ReturnType<>>` pattern for type-safe client usage
Applied to files:
apps/web/app/api/organizations/[organizationId]/route.ts
🧬 Code graph analysis (2)
apps/web/hooks/useOrganization.ts (1)
apps/web/app/api/organizations/[organizationId]/route.ts (1)
OrganizationResponse(6-6)
apps/web/app/api/organizations/[organizationId]/route.ts (2)
apps/web/utils/middleware.ts (1)
withAuth(378-386)apps/web/utils/organizations/access.ts (1)
fetchAndCheckIsAdmin(60-82)
⏰ 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: test
- GitHub Check: Review for correctness
🔇 Additional comments (1)
apps/web/app/api/organizations/[organizationId]/route.ts (1)
8-20: Verify null handling when organization is not found.The implementation correctly uses
withAuthmiddleware andfetchAndCheckIsAdminfor authorization. However,getOrganizationcan returnnullif no organization is found (line 28), and this case is not explicitly handled before returning the response.While
fetchAndCheckIsAdminverifies membership, there's a potential edge case where the organization could be deleted between the admin check and the fetch. Consider whether an explicit null check and error response would improve reliability:const result = await getOrganization({ organizationId }); + +if (!result) { + throw new SafeError("Organization not found"); +} return NextResponse.json(result);
| ); | ||
|
|
||
| return ( | ||
| <Dialog open={isOpen} onOpenChange={onToggle}> |
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.
onOpenChange passes the desired open boolean, but onToggle ignores it and just flips state. This can desync the dialog. Consider handling the boolean (e.g., onOpenChange={(open) => (open ? onOpen() : onClose())}) or update useDialogState to accept and set the boolean directly.
🚀 Reply to ask Macroscope to explain or update this suggestion.
👍 Helpful? React to give us feedback.
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
♻️ Duplicate comments (1)
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts (1)
28-38: Validate numeric query params to prevent Invalid Date in SQL.The
z.coerce.number().nullish()schema can produceNaNwhen given invalid input. SinceNaN ?? undefinedevaluates toNaN, lines 55 and 58 will createnew Date(NaN)(Invalid Date), which will break the SQL query.Apply this diff to validate the params:
- const result = await getEmailVolumeBuckets({ - organizationId, - fromDate: queryParams.fromDate ?? undefined, - toDate: queryParams.toDate ?? undefined, - }); + const fromDate = + queryParams.fromDate != null && Number.isFinite(queryParams.fromDate) + ? queryParams.fromDate + : undefined; + const toDate = + queryParams.toDate != null && Number.isFinite(queryParams.toDate) + ? queryParams.toDate + : undefined; + + const result = await getEmailVolumeBuckets({ + organizationId, + fromDate, + toDate, + });Based on past review comments, this issue was flagged but not yet addressed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts(1 hunks)apps/web/app/api/organizations/[organizationId]/stats/types.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/api/organizations/[organizationId]/stats/types.ts
- apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts
🧰 Additional context used
📓 Path-based instructions (17)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use@/path aliases for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Follow consistent naming conventions using PascalCase for components
Centralize shared types in dedicated type filesImport specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
Files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
apps/web/app/api/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/*.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware for authentication
Export response types from GET API routes usingAwaited<ReturnType<>>pattern for type-safe client usage
Files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
**/*.{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/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
**/{server,api,actions,utils}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{server,api,actions,utils}/**/*.ts: UsecreateScopedLoggerfrom "@/utils/logger" for logging in backend code
Add thecreateScopedLoggerinstantiation at the top of the file with an appropriate scope name
Use.with()method to attach context variables only within specific functions, not on global loggers
For large functions with reused variables, usecreateScopedLogger().with()to attach context once and reuse the logger without passing variables repeatedly
Files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.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/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
🧠 Learnings (10)
📚 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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
📚 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/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 Learning: 2025-11-25T14:36:18.416Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-11-25T14:36:18.416Z
Learning: Applies to apps/web/app/api/**/*.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware for authentication
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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/(ai/digest|resend/digest|clean/gmail|user/categorize/senders/batch)/**/*.{ts,tsx} : QStash endpoints must use `verifySignatureAppRouter` middleware to verify request signatures and prevent request spoofing
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/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 : Use Prisma for database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.tsapps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All related resources in database queries must use proper relationship filtering (not just top-level user/account filtering)
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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 `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts
📚 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 : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/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 : Use Prisma for all database queries in GET API routes
Applied to files:
apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts
🧬 Code graph analysis (1)
apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts (5)
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts (1)
GET(20-42)apps/web/app/api/organizations/[organizationId]/stats/rules-buckets/route.ts (1)
GET(20-42)apps/web/utils/middleware.ts (1)
withAuth(378-386)apps/web/utils/organizations/access.ts (1)
fetchAndCheckIsAdmin(60-82)apps/web/app/api/organizations/[organizationId]/stats/types.ts (2)
orgStatsParams(3-6)OrgStatsParams(7-7)
⏰ 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: Review for correctness
🔇 Additional comments (4)
apps/web/app/api/organizations/[organizationId]/stats/email-buckets/route.ts (2)
1-18: LGTM! Proper setup and type exports.The imports, middleware usage, bucket definitions, and response type export follow project conventions correctly. The bucket labels now accurately reflect the inclusive ranges used in the bucketing logic.
44-94: LGTM! Efficient query and correct bucketing logic.The raw SQL query is properly parameterized, organization-scoped, and the bucketing logic now correctly matches the inclusive range labels.
apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts (2)
1-16: LGTM! Proper authentication and authorization.The route correctly uses
withAuthmiddleware andfetchAndCheckIsAdminfor access control, following project security patterns.Based on learnings, GET API routes properly use
withAuthmiddleware for authentication.
34-99: LGTM! Efficient aggregation with proper scoping.The implementation efficiently computes all three metrics in a single SQL query with proper organization scoping in each subquery. The use of different date columns (
em.datefor emails,er.createdAtfor rules) is appropriate, and bigint-to-number conversion includes safe null handling.
| const { searchParams } = new URL(request.url); | ||
| const queryParams = orgStatsParams.parse({ | ||
| fromDate: searchParams.get("fromDate"), | ||
| toDate: searchParams.get("toDate"), | ||
| }); | ||
|
|
||
| const result = await getTotals({ | ||
| organizationId, | ||
| fromDate: queryParams.fromDate ?? undefined, | ||
| toDate: queryParams.toDate ?? undefined, | ||
| }); |
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.
Validate numeric query params to prevent Invalid Date in SQL.
Same issue as in the email-buckets route: z.coerce.number().nullish() can produce NaN, which becomes Invalid Date when passed to new Date() at lines 48, 51, 62, and 66, potentially breaking the SQL query.
Apply this diff:
- const result = await getTotals({
- organizationId,
- fromDate: queryParams.fromDate ?? undefined,
- toDate: queryParams.toDate ?? undefined,
- });
+ const fromDate =
+ queryParams.fromDate != null && Number.isFinite(queryParams.fromDate)
+ ? queryParams.fromDate
+ : undefined;
+ const toDate =
+ queryParams.toDate != null && Number.isFinite(queryParams.toDate)
+ ? queryParams.toDate
+ : undefined;
+
+ const result = await getTotals({
+ organizationId,
+ fromDate,
+ toDate,
+ });🤖 Prompt for AI Agents
In apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts around
lines 18 to 28, the numeric query params are being coerced in a way that can
produce NaN (which later becomes Invalid Date in SQL); update the parsing and
validation so fromDate/toDate are only numbers when valid: change the zod schema
(orgStatsParams) to preprocess the raw string into a number and then refine with
z.number().int().finite().nullable().optional() (or use z.preprocess to parseInt
and then z.number().refine(Number.isFinite)), or explicitly parse the
searchParams values here and if parse yields NaN treat them as undefined or
throw a 400. Ensure you pass undefined (not NaN) to getTotals so downstream new
Date() calls never receive NaN.
Add an organization analytics view and tabs header to organization pages with routes and APIs for totals and bucketed stats
Introduce an org-level Analytics page with a shared tabs header. Add client hooks and API routes for totals and bucketed email and rule stats, and render charts and KPI cards with date-range filters.
📍Where to Start
Start with the server route for totals at
GET /api/organizations/[organizationId]/stats/totalsin apps/web/app/api/organizations/[organizationId]/stats/totals/route.ts, then review the UI entry at apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx.Macroscope summarized 22c85ea.
Summary by CodeRabbit
New Features
Bug Fixes / UX
Style
Chore
✏️ Tip: You can customize this high-level summary in your review settings.