Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis update introduces digest category management and frequency scheduling for email digests, refactors onboarding and digest-related UI to use new APIs, and enforces email account ownership checks across multiple pages. It adds new API endpoints, utility functions, and validation schemas, while removing digest logic from the categories onboarding flow and updating the application version. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CategoriesPage
participant API
participant DB
User->>CategoriesPage: Open Digest Frequency Page
CategoriesPage->>API: GET /api/user/digest-settings
API->>DB: Query digest settings for account
DB-->>API: Digest settings data
API-->>CategoriesPage: Digest settings JSON
User->>CategoriesPage: Toggle categories / Set frequency
CategoriesPage->>API: POST updateDigestCategoriesAction
API->>DB: Update digest settings
DB-->>API: Success
API-->>CategoriesPage: Success response
sequenceDiagram
participant User
participant DigestFrequencyDialog
participant API
participant DB
User->>DigestFrequencyDialog: Open dialog, select schedule
DigestFrequencyDialog->>API: POST saveDigestScheduleAction
API->>DB: Update schedule for account
DB-->>API: Success
API-->>DigestFrequencyDialog: Success response
DigestFrequencyDialog->>User: Show success toast, close dialog
Possibly related PRs
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxOops! Something went wrong! :( ESLint: 9.28.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxOops! Something went wrong! :( ESLint: 9.28.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsxOops! Something went wrong! :( ESLint: 9.28.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
apps/web/utils/actions/settings.ts (1)
105-197: Well-structured action with good separation of concerns.The implementation correctly separates
coldEmail(stored onemailAccount) from rule-based categories. The parallel execution withPromise.allis efficient.However, consider adding error handling for the individual promise operations to prevent one category update failure from affecting others.
Consider wrapping individual promises in try-catch to handle partial failures:
const promise = async () => { + try { const rule = await prisma.rule.findUnique({ // ... existing code }); // ... rest of the logic + } catch (error) { + console.error(`Failed to update digest for ${key}:`, error); + // Could track which categories failed for better user feedback + } };apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx (1)
30-37: Consider making default schedule values configurable.The hardcoded default values (weekly, Monday, 11 AM) might not suit all users. Consider making these configurable or fetching user preferences.
You could extract defaults to a configuration object:
+ const DEFAULT_DIGEST_SCHEDULE = { + intervalDays: 7, + daysOfWeek: 1 << (6 - 1), // Monday + timeOfDay: new Date(new Date().setHours(11, 0, 0, 0)), // 11 AM + occurrences: 1, + }; const [digestScheduleValue, setDigestScheduleValue] = useState< SaveDigestScheduleBody["schedule"] - >({ - intervalDays: 7, - daysOfWeek: 1 << (6 - 1), // Monday (1) - timeOfDay: new Date(new Date().setHours(11, 0, 0, 0)), // 11 AM - occurrences: 1, - }); + >(DEFAULT_DIGEST_SCHEDULE);apps/web/app/api/user/onboarding-preferences/route.ts (1)
46-82: Consider simplifying the preference processing logicThe implementation correctly scopes queries to the authenticated account. However, the multi-level processing through helper functions adds complexity. Consider consolidating some of the logic or adding unit tests for these transformations.
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx (1)
165-172: Consider allowing progression without category selectionThe Next button is disabled while loading, which is good. However, consider whether users should be allowed to proceed without selecting any categories, as all fields in the schema are optional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx(3 hunks)apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx(4 hunks)apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/automation/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/clean/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/setup/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/simple/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/usage/page.tsx(1 hunks)apps/web/app/api/user/digest-settings/route.ts(1 hunks)apps/web/app/api/user/onboarding-preferences/route.ts(1 hunks)apps/web/utils/actions/settings.ts(2 hunks)apps/web/utils/actions/settings.validation.ts(2 hunks)apps/web/utils/category-config.tsx(1 hunks)apps/web/utils/email-account.ts(1 hunks)version.txt(1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
`apps/web/**/app/**`: Follow NextJS app router structure by organizing code within the app directory.
apps/web/**/app/**: Follow NextJS app router structure by organizing code within the app directory.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/api/user/digest-settings/route.tsapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/app/api/user/onboarding-preferences/route.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`apps/web/**/*.{ts,tsx}`: Use TypeScript with strict null checks enabled. Use path aliases with @/ for imports from the project root. Use proper error handling with try/catch block...
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks enabled.
Use path aliases with @/ for imports from the project root.
Use proper error handling with try/catch blocks.
Use the LoadingContent component for async data loading states.
Prefix client-side environment variables with NEXT_PUBLIC_.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/utils/email-account.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/api/user/digest-settings/route.tsapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.tsapps/web/app/api/user/onboarding-preferences/route.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/utils/category-config.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`apps/web/**/*.{ts,tsx,js,jsx}`: Format code with Prettier and follow tailwindcss patterns using prettier-plugin-tailwindcss.
apps/web/**/*.{ts,tsx,js,jsx}: Format code with Prettier and follow tailwindcss patterns using prettier-plugin-tailwindcss.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/utils/email-account.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/api/user/digest-settings/route.tsapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.tsapps/web/app/api/user/onboarding-preferences/route.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/utils/category-config.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`apps/web/**`: Install packages only within the 'apps/web' directory, not at the repository root.
apps/web/**: Install packages only within the 'apps/web' directory, not at the repository root.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/utils/email-account.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/api/user/digest-settings/route.tsapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.tsapps/web/app/api/user/onboarding-preferences/route.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/utils/category-config.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`apps/web/app/(app)/**/*.{js,jsx,ts,tsx}`: If you need to use onClick in a component, that component is a client component and file must start with 'use client'.
apps/web/app/(app)/**/*.{js,jsx,ts,tsx}: If you need to use onClick in a component, that component is a client component and file must start with 'use client'.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`**/*.{js,jsx,ts,tsx}`: Use Shadcn UI and Tailwind for components and styling. Implement responsive design with Tailwind CSS using a mobile-first approach. Use the `next/image` pac...
**/*.{js,jsx,ts,tsx}: Use Shadcn UI and Tailwind for components and styling.
Implement responsive design with Tailwind CSS using a mobile-first approach.
Use thenext/imagepackage for images.
apps/web/app/(app)/[emailAccountId]/usage/page.tsxapps/web/app/(app)/[emailAccountId]/clean/run/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsxapps/web/app/(app)/[emailAccountId]/clean/page.tsxapps/web/app/(app)/[emailAccountId]/smart-categories/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/page.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/simple/page.tsxapps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsxapps/web/utils/email-account.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsxapps/web/app/(app)/[emailAccountId]/automation/page.tsxapps/web/app/(app)/[emailAccountId]/setup/page.tsxapps/web/app/api/user/digest-settings/route.tsapps/web/app/(app)/[emailAccountId]/simple/completed/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/page.tsxapps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.tsapps/web/app/api/user/onboarding-preferences/route.tsapps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsxapps/web/utils/category-config.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsxapps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx
`apps/web/utils/**/*`: Create utility functions in utils/ folder for reusable logic.
apps/web/utils/**/*: Create utility functions in utils/ folder for reusable logic.
apps/web/utils/email-account.tsapps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.tsapps/web/utils/category-config.tsx
`apps/web/app/api/**/*`: All API route handlers must use authentication middleware such as withAuth, withEmailAccount, or withError with custom authentication logic. All database q...
apps/web/app/api/**/*: All API route handlers must use authentication middleware such as withAuth, withEmailAccount, or withError with custom authentication logic.
All database queries must include user/account filtering, using emailAccountId or userId in WHERE clauses.
Parameters must be validated before use; do not use direct parameter values in queries without validation.
Request bodies should use Zod schemas for validation.
Only necessary fields should be returned in API responses; use Prisma's select to limit fields.
Do not include sensitive data in error messages; use generic errors and SafeError for user-facing errors.
Cron endpoints must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak secrets in cron endpoints; secrets should not be plain strings in code except for environment variables like CRON_SECRET.
apps/web/app/api/user/digest-settings/route.tsapps/web/app/api/user/onboarding-preferences/route.ts
`**/api/**/*.ts`: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware such as withAuth or withEmailAccount. ALL database queries i...
**/api/**/*.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware such as withAuth or withEmailAccount.
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources being accessed or modified belong to the authenticated user before performing operations.
All parameters (route, query, body) in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas or equivalent.
Error responses in API routes MUST NOT leak sensitive information; use generic error messages and consistent error formats.
All findUnique/findFirst database calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany database calls in API routes MUST be scoped to the authenticated user's data.
API routes MUST NOT return sensitive fields or data from other users.
API routes MUST NOT use direct object references (IDs) without ownership checks to prevent IDOR vulnerabilities.
API routes MUST use explicit whitelisting of allowed fields for updates to prevent mass assignment and privilege escalation.
API routes MUST NOT use user input directly in queries; always validate and sanitize inputs.
API routes MUST use SafeError or equivalent for error handling to prevent information disclosure.
API routes MUST use withError middleware (not withAuth or withEmailAccount) for public endpoints, webhooks, or cron endpoints, and MUST implement custom authentication/validation as appropriate.
apps/web/app/api/user/digest-settings/route.tsapps/web/app/api/user/onboarding-preferences/route.ts
`apps/web/utils/actions/**/*`: Server actions are in apps/web/utils/actions folder.
apps/web/utils/actions/**/*: Server actions are in apps/web/utils/actions folder.
apps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.ts
`apps/web/utils/actions/*`: Server action files must start with 'use server'.
apps/web/utils/actions/*: Server action files must start with 'use server'.
apps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.ts
`apps/web/utils/actions/*.ts`: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Use the ap...
apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Use the appropriate client (actionClientUser, actionClient, or adminActionClient) as needed.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations).
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring and instrumentation.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed for cache invalidation.
apps/web/utils/actions/settings.tsapps/web/utils/actions/settings.validation.ts
`apps/web/utils/actions/*.validation.ts`: Define input validation schemas using Zod in the corresponding .validation.ts file. These schemas are used by next-safe-action (.schema())...
apps/web/utils/actions/*.validation.ts: Define input validation schemas using Zod in the corresponding .validation.ts file. These schemas are used by next-safe-action (.schema()) and can also be reused on the client for form validation.
apps/web/utils/actions/settings.validation.ts
🔇 Additional comments (28)
apps/web/utils/email-account.ts (1)
5-19: LGTM! Solid authorization utility implementation.The function correctly implements email account ownership verification by:
- Validating user authentication
- Querying with both emailAccountId and userId to ensure ownership
- Using appropriate error handling with
notFound()The minimal database query (selecting only
id) is efficient for authorization purposes.version.txt (1)
1-1: Version bump looks good.Appropriate version increment for the new authorization features.
apps/web/app/(app)/[emailAccountId]/automation/page.tsx (2)
22-22: Good addition of authorization import.Consistent with the security pattern being implemented across the application.
32-32: Authorization check correctly placed.The ownership verification happens early in the request lifecycle, before any business logic or data fetching, which is the correct security practice.
apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx (2)
17-17: Consistent authorization import.Good addition of the security utility import.
30-31: Authorization check properly implemented.The ownership verification is correctly placed before any data fetching or business logic, ensuring secure access control.
apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx (2)
29-29: Authorization import follows established pattern.Consistent with the security implementation across other pages.
40-40: Well-positioned authorization check.The ownership verification correctly occurs before the Promise.all data fetching operations, ensuring proper access control.
apps/web/app/(app)/[emailAccountId]/assistant/page.tsx (1)
10-10: Good security enhancement!The addition of the ownership check is a proper security measure that ensures users can only access email accounts they own. The placement is appropriate - early in the function execution but after parameter extraction.
Also applies to: 20-20
apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx (1)
9-9: Excellent improvements - security + code quality!The changes include:
- Security enhancement: Added ownership verification to prevent unauthorized access
- Code improvement: Cleaner parameter extraction using destructuring
Both changes follow established patterns and improve the codebase.
Also applies to: 15-16
apps/web/app/(app)/[emailAccountId]/usage/page.tsx (1)
5-5: Consistent security implementation!The changes properly implement the ownership verification pattern that's being applied across the codebase. The destructuring also improves code readability.
Also applies to: 10-11
apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx (1)
11-11: Security pattern consistently applied!The ownership verification is properly implemented before any Gmail API calls or business logic. The parameter destructuring also improves code clarity.
Also applies to: 27-28
apps/web/app/(app)/[emailAccountId]/setup/page.tsx (1)
17-17: Proper authorization check implemented!The ownership verification follows the established security pattern and is correctly placed before any database operations.
Also applies to: 23-23
apps/web/app/(app)/[emailAccountId]/clean/page.tsx (1)
6-6: LGTM: Proper security implementationThe ownership verification is correctly implemented by importing the utility function and calling it early in the page logic before any data fetching occurs. This ensures users can only access their own email account data.
Also applies to: 14-14
apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx (1)
2-2: LGTM: Security check and improved parameter handlingGood implementation of the ownership verification pattern. The destructuring of
emailAccountIdalso improves code readability and follows modern JavaScript best practices. The security check is properly placed before the database query.Also applies to: 9-10, 14-14
apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx (1)
5-5: LGTM: Consistent security implementationThe ownership verification follows the established pattern correctly, with the check performed before fetching user categories. The parameter destructuring is clean and consistent with the other files in this PR.
Also applies to: 10-11
apps/web/app/(app)/[emailAccountId]/simple/page.tsx (1)
15-15: LGTM: Early authorization check for complex pageExcellent placement of the ownership verification before any Gmail API calls or data processing. This ensures unauthorized users are rejected early in the request lifecycle, which is especially important for this page that performs multiple external API operations.
Also applies to: 23-24
apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx (2)
13-13: LGTM: Comprehensive security implementationWell-implemented ownership verification with proper early placement before any data fetching operations. The consistent use of the destructured
emailAccountIdvariable throughout the function improves code maintainability.Also applies to: 18-19
22-22: LGTM: Consistent variable usageGood use of the destructured
emailAccountIdvariable consistently throughout the function, replacing the previousparams.emailAccountIdreferences. This follows the established pattern and improves code readability.Also applies to: 28-28, 40-40
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx (1)
118-127: LGTM! Clean refactor to centralized category configuration.The dynamic rendering using
categoryConfig.map()is a good improvement over hardcoded category cards. The props are correctly mapped and the component interface remains consistent.apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx (1)
44-68: Excellent error handling implementation.The error handling covers both server errors and exceptions properly, with appropriate user feedback via toast notifications. The loading state management is also correct.
apps/web/app/api/user/digest-settings/route.ts (1)
10-15: Proper authentication and scoping implemented.The route correctly uses
withEmailAccountmiddleware and scopes queries to the authenticated email account. Good security practices followed.apps/web/utils/category-config.tsx (2)
11-56: Excellent centralization of category configuration.This centralized configuration improves maintainability and consistency across the codebase. The tooltips are informative and the icon styling is consistent.
58-58: Good type safety with CategoryKey export.The exported
CategoryKeytype provides good type safety for components consuming this configuration.apps/web/utils/actions/settings.validation.ts (2)
54-65: LGTM!The new digest categories schema is well-structured with optional fields allowing flexible partial updates.
12-12: ```shell
#!/bin/bashLocate and display the DigestMailFrequencySection component to inspect schedule handling
fd -t f 'DigestMailFrequencySection.tsx' apps/web/app | xargs -I {} sed -n '1,200p' {}
Locate and display the DigestFrequencyDialog component for additional context
fd -t f 'DigestFrequencyDialog.tsx' apps/web/app | xargs -I {} sed -n '1,200p' {}
</details> <details> <summary>apps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx (2)</summary> `36-54`: **Well-implemented state management** The data fetching with SWR and state synchronization via useEffect is properly implemented. The loading states are handled correctly throughout the component. --- `68-91`: **Excellent error handling implementation** The finish handler properly manages loading states, provides clear user feedback via toasts, and handles both server errors and exceptions appropriately. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| const systemTypeToKey: Record<SystemType, keyof typeof digestSettings> = { | ||
| [SystemType.TO_REPLY]: "toReply", | ||
| [SystemType.NEWSLETTER]: "newsletter", | ||
| [SystemType.MARKETING]: "marketing", | ||
| [SystemType.CALENDAR]: "calendar", | ||
| [SystemType.RECEIPT]: "receipt", | ||
| [SystemType.NOTIFICATION]: "notification", | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Incomplete SystemType mapping.
The systemTypeToKey mapping doesn't include all SystemTypes from the query filter. This could cause issues if new SystemTypes are added to the query but not to the mapping.
Consider making the mapping more maintainable:
- const systemTypeToKey: Record<SystemType, keyof typeof digestSettings> = {
- [SystemType.TO_REPLY]: "toReply",
- [SystemType.NEWSLETTER]: "newsletter",
- [SystemType.MARKETING]: "marketing",
- [SystemType.CALENDAR]: "calendar",
- [SystemType.RECEIPT]: "receipt",
- [SystemType.NOTIFICATION]: "notification",
- };
+ const SUPPORTED_SYSTEM_TYPES = [
+ SystemType.TO_REPLY,
+ SystemType.NEWSLETTER,
+ SystemType.MARKETING,
+ SystemType.CALENDAR,
+ SystemType.RECEIPT,
+ SystemType.NOTIFICATION,
+ ] as const;
+
+ const systemTypeToKey: Record<typeof SUPPORTED_SYSTEM_TYPES[number], keyof typeof digestSettings> = {
+ [SystemType.TO_REPLY]: "toReply",
+ [SystemType.NEWSLETTER]: "newsletter",
+ [SystemType.MARKETING]: "marketing",
+ [SystemType.CALENDAR]: "calendar",
+ [SystemType.RECEIPT]: "receipt",
+ [SystemType.NOTIFICATION]: "notification",
+ };Then use SUPPORTED_SYSTEM_TYPES in the query filter at line 29-36 to ensure consistency.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const systemTypeToKey: Record<SystemType, keyof typeof digestSettings> = { | |
| [SystemType.TO_REPLY]: "toReply", | |
| [SystemType.NEWSLETTER]: "newsletter", | |
| [SystemType.MARKETING]: "marketing", | |
| [SystemType.CALENDAR]: "calendar", | |
| [SystemType.RECEIPT]: "receipt", | |
| [SystemType.NOTIFICATION]: "notification", | |
| }; | |
| const SUPPORTED_SYSTEM_TYPES = [ | |
| SystemType.TO_REPLY, | |
| SystemType.NEWSLETTER, | |
| SystemType.MARKETING, | |
| SystemType.CALENDAR, | |
| SystemType.RECEIPT, | |
| SystemType.NOTIFICATION, | |
| ] as const; | |
| const systemTypeToKey: Record<typeof SUPPORTED_SYSTEM_TYPES[number], keyof typeof digestSettings> = { | |
| [SystemType.TO_REPLY]: "toReply", | |
| [SystemType.NEWSLETTER]: "newsletter", | |
| [SystemType.MARKETING]: "marketing", | |
| [SystemType.CALENDAR]: "calendar", | |
| [SystemType.RECEIPT]: "receipt", | |
| [SystemType.NOTIFICATION]: "notification", | |
| }; |
🤖 Prompt for AI Agents
In apps/web/app/api/user/digest-settings/route.ts around lines 29 to 36 and 75
to 82, the systemTypeToKey mapping is incomplete and may cause issues if new
SystemTypes are added. Define a constant array SUPPORTED_SYSTEM_TYPES that
includes all SystemTypes used in the query filter and systemTypeToKey mapping.
Replace the hardcoded SystemTypes in both the query filter and the mapping with
this constant to ensure consistency and maintainability.
| const { data: defaultValues, isLoading } = | ||
| useSWR<GetOnboardingPreferencesResponse>( | ||
| "/api/user/onboarding-preferences", | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for API failures
The SWR hook should handle error states to provide user feedback when the API request fails.
- const { data: defaultValues, isLoading } =
+ const { data: defaultValues, isLoading, error } =
useSWR<GetOnboardingPreferencesResponse>(
"/api/user/onboarding-preferences",
);Then add an error state UI after the loading check:
if (error) {
return (
<Card className="my-4 w-full max-w-2xl p-6 sm:mx-4 md:mx-auto">
<div className="text-center space-y-4">
<p className="text-destructive">Failed to load onboarding preferences</p>
<Button onClick={() => window.location.reload()}>Retry</Button>
</div>
</Card>
);
}🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx around
lines 13 to 16, the useSWR hook is missing error handling for API failures.
Update the hook to destructure the error object from useSWR, then add a
conditional check after the loading state to render an error UI. This UI should
display a failure message and a retry button that reloads the page, providing
clear user feedback on API request failures.
Summary by CodeRabbit
New Features
Improvements
Security
Bug Fixes
Chores