Skip to content

Move to digest to its own set up screen#520

Merged
elie222 merged 2 commits intomainfrom
feat/digest-screen
Jun 23, 2025
Merged

Move to digest to its own set up screen#520
elie222 merged 2 commits intomainfrom
feat/digest-screen

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Jun 23, 2025

Summary by CodeRabbit

  • New Features

    • Added a modal dialog for configuring digest email frequency.
    • Introduced dynamic category configuration for email categories with icons and tooltips.
    • Added toggles for selecting which categories are included in digest emails.
    • New API endpoints for retrieving and updating onboarding preferences and digest settings.
  • Improvements

    • Enhanced onboarding and category setup with a more dynamic, data-driven UI.
    • Updated onboarding flow to fetch preferences from an API, improving responsiveness and maintainability.
    • Refined digest category management to allow easier customization via toggles and dialog.
  • Security

    • Added ownership verification for all email account-related pages to ensure only authorized users can access or modify account data.
  • Bug Fixes

    • Removed outdated or redundant digest-related controls from the category setup form.
  • Chores

    • Updated version to v1.7.4.

@vercel
Copy link

vercel bot commented Jun 23, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
inbox-zero ✅ Ready (Inspect) Visit Preview Jun 23, 2025 1:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 23, 2025

Walkthrough

This 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

File(s) Change Summary
.../assistant/onboarding/CategoriesSetup.tsx Removed all digest-related logic/UI from categories setup; switched to data-driven category rendering.
.../assistant/onboarding/DigestFrequencyDialog.tsx Added new modal dialog component for configuring digest frequency.
.../assistant/onboarding/digest-frequency/page.tsx Refactored to manage digest categories with toggles and frequency dialog; removed direct schedule management.
.../assistant/onboarding/page.tsx Refactored from server to client component; now fetches onboarding preferences via SWR and API.
.../assistant/page.tsx
.../automation/page.tsx
.../clean/onboarding/page.tsx
.../clean/page.tsx
.../clean/run/page.tsx
.../reply-zero/onboarding/page.tsx
.../reply-zero/page.tsx
.../setup/page.tsx
.../simple/completed/page.tsx
.../simple/page.tsx
.../smart-categories/page.tsx
.../smart-categories/setup/page.tsx
.../usage/page.tsx
Added ownership verification for email accounts using new utility function.
.../api/user/digest-settings/route.ts Added new API endpoint to fetch digest settings for categories.
.../api/user/onboarding-preferences/route.ts Added new API endpoint to fetch onboarding preferences for categories.
.../utils/actions/settings.ts Added new action to update digest categories for an email account.
.../utils/actions/settings.validation.ts Added/updated validation schemas for digest categories and schedule; introduced new type alias.
.../utils/category-config.tsx Added new export defining category config array and type alias for category keys.
.../utils/email-account.ts Added utility function to check if user owns a given email account.
version.txt Updated version from v1.7.3 to v1.7.4.

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

Possibly related PRs

  • feat: Digest e-mails #478: Adds digest support to CategoriesSetup with hasDigest property and DigestCheckbox UI, directly conflicting with this PR which removes digest logic from the same component.

Poem

A bunny hops with code so neat,
Digest toggles now complete!
Categories prance in tidy rows,
Ownership checks before it goes.
New dialogs bloom, the schedule set—
v1.7.4, the best one yet!
🐇✨

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx

Oops! 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.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx

Oops! 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.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

apps/web/app/(app)/[emailAccountId]/assistant/onboarding/digest-frequency/page.tsx

Oops! 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.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

  • 20 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 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 on emailAccount) from rule-based categories. The parallel execution with Promise.all is 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 logic

The 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 selection

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5ce2505 and c626128.

📒 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.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/api/user/digest-settings/route.ts
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/app/api/user/onboarding-preferences/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/utils/email-account.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/api/user/digest-settings/route.ts
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/utils/actions/settings.ts
  • apps/web/utils/actions/settings.validation.ts
  • apps/web/app/api/user/onboarding-preferences/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/utils/category-config.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/utils/email-account.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/api/user/digest-settings/route.ts
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/utils/actions/settings.ts
  • apps/web/utils/actions/settings.validation.ts
  • apps/web/app/api/user/onboarding-preferences/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/utils/category-config.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/utils/email-account.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/api/user/digest-settings/route.ts
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/utils/actions/settings.ts
  • apps/web/utils/actions/settings.validation.ts
  • apps/web/app/api/user/onboarding-preferences/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/utils/category-config.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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 the next/image package for images.

  • apps/web/app/(app)/[emailAccountId]/usage/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/run/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/setup/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/page.tsx
  • apps/web/app/(app)/[emailAccountId]/smart-categories/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/page.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/onboarding/page.tsx
  • apps/web/app/(app)/[emailAccountId]/simple/page.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx
  • apps/web/utils/email-account.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/automation/page.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/page.tsx
  • apps/web/app/api/user/digest-settings/route.ts
  • apps/web/app/(app)/[emailAccountId]/simple/completed/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
  • apps/web/utils/actions/settings.ts
  • apps/web/utils/actions/settings.validation.ts
  • apps/web/app/api/user/onboarding-preferences/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/DigestFrequencyDialog.tsx
  • apps/web/utils/category-config.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx
  • apps/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.ts
  • apps/web/utils/actions/settings.ts
  • apps/web/utils/actions/settings.validation.ts
  • apps/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.ts
  • apps/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.ts
  • apps/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.ts
  • apps/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.ts
  • apps/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.ts
  • apps/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:

  1. Security enhancement: Added ownership verification to prevent unauthorized access
  2. 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 implementation

The 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 handling

Good implementation of the ownership verification pattern. The destructuring of emailAccountId also 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 implementation

The 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 page

Excellent 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 implementation

Well-implemented ownership verification with proper early placement before any data fetching operations. The consistent use of the destructured emailAccountId variable throughout the function improves code maintainability.

Also applies to: 18-19


22-22: LGTM: Consistent variable usage

Good use of the destructured emailAccountId variable consistently throughout the function, replacing the previous params.emailAccountId references. 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 withEmailAccount middleware 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 CategoryKey type 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/bash

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

Comment on lines +75 to +82
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",
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

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

Comment on lines +13 to +16
const { data: defaultValues, isLoading } =
useSWR<GetOnboardingPreferencesResponse>(
"/api/user/onboarding-preferences",
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

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.

@elie222 elie222 merged commit e64f3fb into main Jun 23, 2025
10 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jul 11, 2025
@coderabbitai coderabbitai bot mentioned this pull request Aug 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments