Skip to content

Comments

feat: Delayed actions#519

Merged
elie222 merged 30 commits intoelie222:mainfrom
edulelis:delayed-actions
Jul 21, 2025
Merged

feat: Delayed actions#519
elie222 merged 30 commits intoelie222:mainfrom
edulelis:delayed-actions

Conversation

@edulelis
Copy link
Collaborator

@edulelis edulelis commented Jun 23, 2025

Summary by CodeRabbit

  • New Features

    • Introduced delayed actions for rules, allowing users to schedule actions (like archive, label, reply, etc.) to execute after a configurable delay (1 minute to 90 days).
    • Added UI controls to set action delays when creating or editing rules.
    • Included a new "Label + archive after a week" option during onboarding, when available.
    • Users can view and manage delayed actions in relevant rule and action interfaces.
  • Bug Fixes

    • Improved tooltip positioning and consistency in UI components.
  • Documentation

    • Added documentation describing the delayed actions feature, its use cases, and scheduling details.
  • Tests

    • Added comprehensive tests for delayed action scheduling, execution, and cancellation.
  • Chores

    • Updated validation and schema definitions to support delay configuration and new scheduling logic.

@vercel
Copy link

vercel bot commented Jun 23, 2025

@edulelis is attempting to deploy a commit to the Inbox Zero Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 23, 2025

Walkthrough

This update introduces delayed execution for rule actions in the email assistant application. It adds a delayInMinutes field to action models, updates UI components to allow users to configure delays, and implements backend scheduling and execution using a new ScheduledAction model and integration with QStash for reliable delayed processing. Validation, optimistic UI updates, and database schema changes support these enhancements.

Changes

File(s) / Group Change Summary
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx, RuleDialog.tsx, Rules.tsx, DraftReplies.tsx Added support for delayed actions in rule forms, optimistic updates, UI controls for delay, and propagation of mutate function for updates.
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx Added "Label + archive after a week" option conditionally based on delayed actions feature flag.
apps/web/components/TooltipExplanation.tsx, components/ui/tooltip.tsx Refactored tooltip components for controlled open state, added positioning prop, and updated styling.
apps/web/hooks/useFeatureFlags.ts Added useDelayedActionsEnabled hook for feature flag.
apps/web/prisma/schema.prisma, prisma/migrations/20250626043046_add_scheduled_actions/migration.sql Added ScheduledAction model, enums for status, delayInMinutes to Action, and related schema changes.
apps/web/utils/date.ts Added exported constants for common time intervals in minutes.
apps/web/utils/actions/rule.validation.ts, ai/rule/create-rule-schema.ts Added delayInMinutes validation schema and integrated into action schemas.
apps/web/utils/ai/types.ts, utils/action-item.ts, utils/rule/rule.ts Extended types and mapping functions to include delayInMinutes.
apps/web/utils/actions/rule.ts Integrated delayed archive action into onboarding and rule creation/update logic.
apps/web/utils/ai/choose-rule/run-rules.ts Separated immediate and delayed actions, scheduled delayed ones, and cancelled superseded schedules.
apps/web/utils/ai/assistant/chat.ts Integrated delayInMinutes into rule actions update logic.
apps/web/utils/delayed-actions.ts Added utility to determine if an action type supports delay.
apps/web/utils/scheduled-actions/scheduler.ts, executor.ts Implemented scheduler and executor for delayed actions, QStash integration, cancellation, and status management.
apps/web/utils/scheduled-actions/scheduler.test.ts, executor.test.ts Added unit tests for scheduler and executor logic, including cancellation and error handling.
apps/web/app/api/scheduled-actions/execute/route.ts Added API route for QStash-triggered scheduled action execution with signature verification and error handling.
.cursor/rules/features/delayed-actions.mdc Added documentation for delayed actions feature and its implementation.
apps/web/__tests__/ai-choose-rule.test.ts Added delayInMinutes property to test action for coverage.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant Backend
    participant QStash
    participant Executor

    User->>UI: Configure rule with delayed action
    UI->>Backend: Save rule (with delayInMinutes)
    Backend->>Backend: Persist rule and action
    Backend->>QStash: Schedule delayed action (with notBefore)
    QStash->>Backend: POST /api/scheduled-actions/execute (at scheduled time)
    Backend->>Executor: Mark action as executing
    Executor->>Backend: Execute scheduled action
    Executor->>Backend: Update status (completed/failed)
Loading

Estimated code review effort

4 (60–120 minutes)

The changes span multiple layers (UI, validation, types, backend, scheduling, execution, and tests), introduce a new model and API, and require careful review for correctness, consistency, and security, especially around scheduling, cancellation, and error handling.

Poem

🐇
A hop, a skip, a scheduled leap—
Now actions wait, not just repeat.
With minutes set, or days to snooze,
Your emails rest until you choose.
Delayed or swift, the rules obey,
The rabbit’s code—now time’s in play!
⏳✨

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

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.

@edulelis
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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 (5)
apps/web/app/api/cron/scheduled-actions/route.ts (1)

6-6: Use import type for NextRequest.

The static analysis tool correctly identifies that NextRequest is only used as a type. Consider using import type for better tree-shaking.

-import { NextRequest } from "next/server";
+import type { NextRequest } from "next/server";
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

1450-1451: Consider more robust number parsing.

The current implementation might not handle all edge cases properly.

Consider using a more defensive approach:

-    const numValue = Number.parseInt(value, 10);
-    if (Number.isNaN(numValue) || numValue <= 0) return null;
+    const trimmedValue = value.trim();
+    if (!trimmedValue || !/^\d+$/.test(trimmedValue)) return null;
+    const numValue = Number.parseInt(trimmedValue, 10);
+    if (Number.isNaN(numValue) || numValue <= 0) return null;

This ensures only valid numeric strings are parsed.

apps/web/utils/scheduled-actions/executor.ts (1)

155-157: Consider using static import instead of dynamic import.

Dynamic imports add overhead and complexity when not needed.

Move to top-level import:

+import { parseMessage } from "@/utils/mail";

 // In the function:
-    const parsedMessage = await import("@/utils/mail").then((m) =>
-      m.parseMessage(message),
-    );
+    const parsedMessage = await parseMessage(message);
apps/web/utils/scheduled-actions/scheduler.ts (1)

83-136: Consider using database transactions for consistency.

When scheduling multiple delayed actions, consider wrapping the operations in a transaction to ensure all-or-nothing behavior.

Example approach:

const scheduledActions = await prisma.$transaction(async (tx) => {
  const actions = [];
  for (const actionItem of delayedActions) {
    const scheduledFor = new Date(
      emailInternalDate.getTime() + actionItem.delayInMinutes! * 60 * 1000,
    );
    
    const action = await tx.scheduledAction.create({
      data: { /* ... */ }
    });
    actions.push(action);
  }
  return actions;
});

This ensures either all actions are scheduled or none are, preventing partial scheduling on errors.

apps/web/prisma/schema.prisma (1)

348-348: Consider adding a comment about delay constraints

The documentation mentions delays should be between 1 minute and 30 days (43200 minutes). Consider adding a comment here to document these constraints for future developers.

-  delayInMinutes Int? // Optional delay in minutes (1 min to 30 days)
+  delayInMinutes Int? // Optional delay in minutes (min: 1, max: 43200 = 30 days)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2a7abd and 728435a.

📒 Files selected for processing (12)
  • DELAYED_ACTIONS.mdc (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (3 hunks)
  • apps/web/app/api/cron/scheduled-actions/route.ts (1 hunks)
  • apps/web/prisma/schema.prisma (7 hunks)
  • apps/web/utils/actions/rule.validation.ts (1 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (5 hunks)
  • apps/web/utils/ai/types.ts (1 hunks)
  • apps/web/utils/delayed-actions.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/web/app/api/cron/scheduled-actions/route.ts

[error] 6-6: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

🔇 Additional comments (21)
apps/web/utils/delayed-actions.ts (1)

1-17: LGTM! Clean and well-structured utility module.

The implementation correctly defines the supported delayed action types and provides a type-safe validation function. The documentation is clear and the code follows good practices.

apps/web/utils/ai/types.ts (1)

25-25: LGTM! Appropriate type addition for delayed actions.

The optional and nullable delayInMinutes field is correctly typed and well-documented, maintaining backward compatibility while supporting the new delayed actions feature.

apps/web/utils/actions/rule.validation.ts (1)

76-76: LGTM! Appropriate validation constraints for delay field.

The validation correctly enforces a 1-minute minimum and 30-day maximum (43,200 minutes) for delayed actions, with proper optional/nullable typing that matches the type definitions.

apps/web/utils/scheduled-actions/scheduler.test.ts (1)

1-94: Excellent test coverage for scheduler functionality.

The test suite is comprehensive and well-structured, covering both positive and negative scenarios for action type validation and scheduled action cancellation. The proper mocking and clear assertions provide good confidence in the scheduler implementation.

apps/web/app/api/cron/scheduled-actions/route.ts (1)

20-102: Robust cron job implementation with excellent error handling.

The handler is well-implemented with:

  • Proper authorization and logging
  • Sequential processing to avoid race conditions
  • Comprehensive error handling at multiple levels
  • Good result tracking and monitoring
  • Appropriate timeout configuration (5 minutes)

The duplicate processing prevention using markActionAsExecuting is a nice touch for reliability.

apps/web/utils/scheduled-actions/executor.test.ts (2)

1-52: Well-structured test setup with comprehensive mocking.

The test setup properly mocks all dependencies and provides a complete mock scheduled action object with all required fields.


53-213: Comprehensive test coverage for executor functionality.

The test cases effectively cover:

  • Successful execution with proper status updates
  • Error handling with retry logic
  • Permanent failure scenarios

Good job on testing both the happy path and error conditions.

apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (2)

94-94: Clean integration of delay input for supported actions.

The conditional rendering using isSupportedDelayedAction ensures the delay input only appears for action types that support delays.

Also applies to: 1227-1237


1409-1531: Well-implemented delay input component with proper unit conversion.

The component correctly handles:

  • Conversion between minutes, hours, and days
  • Validation constraints (1 minute to 30 days)
  • Bidirectional sync between display value and stored minutes
  • Hidden input for form integration

Good UX with the unit selector and clear max limit display.

apps/web/utils/scheduled-actions/executor.ts (1)

20-362: Solid implementation of scheduled action execution with robust error handling.

The executor properly handles:

  • Email validation before execution
  • Error classification (permanent vs transient)
  • Retry logic with appropriate delays
  • Status updates and completion tracking
  • Comprehensive logging for monitoring

Good architectural decisions on retry attempts and error categorization.

apps/web/utils/ai/choose-rule/run-rules.ts (2)

102-110: Good practice: Cancel superseded scheduled actions.

Canceling existing scheduled actions before applying new rules prevents duplicate or conflicting actions. The test mode check appropriately skips this in test scenarios.


112-149: Well-structured separation of immediate and delayed action handling.

The implementation correctly:

  • Filters actions based on delay criteria
  • Saves only immediate actions to ExecutedActions
  • Schedules delayed actions separately with proper metadata
  • Sets appropriate status (SCHEDULED vs PENDING) based on action types

This maintains a clean separation between immediate execution and scheduled execution paths.

Also applies to: 224-228

apps/web/utils/scheduled-actions/scheduler.ts (3)

32-36: Good validation of supported action types.

Early validation with a clear error message prevents invalid scheduled actions from being created.


113-113: Safe use of non-null assertion.

The non-null assertion on delayInMinutes! is safe here because the filter on lines 99-103 ensures it's defined and greater than 0.


1-246: Well-implemented scheduler service with comprehensive functionality.

The scheduler properly handles:

  • Action validation and type checking
  • Scheduling with proper time calculations
  • Cancellation of superseded actions
  • Retrieval of due actions for execution
  • Concurrency control with status updates

Good architectural decisions and error handling throughout.

apps/web/prisma/schema.prisma (3)

122-122: Is digestSchedule related to the delayed actions feature?

This field appears to be for digest email scheduling rather than the delayed actions feature that this PR implements. Consider moving this to a separate PR to keep changes focused.


468-469: Well-designed indexes for performance

The indexes are properly optimized for the cron job queries and cancellation operations. Good performance considerations.


846-846: Appropriate status addition for delayed actions

The SCHEDULED status properly represents rules with pending delayed actions, aligning with the documented status flow.

DELAYED_ACTIONS.mdc (3)

120-120: ****


298-315: Excellent production readiness documentation

The performance metrics and deployment checklist provide valuable operational guidance. The future enhancement ideas are well thought out.


251-252: Clarify WIP status vs. completion claims

The document claims the implementation is 100% complete, but the PR title indicates "WIP: Delayed actions". Please clarify whether this is still a work in progress or ready for review.

@edulelis edulelis marked this pull request as ready for review June 25, 2025 05:32
@edulelis edulelis changed the title WIP: Delayed actions feat: Delayed actions Jun 25, 2025
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

♻️ Duplicate comments (3)
apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts (1)

1-1: Fix import type usage for NextRequest.

Same issue as the cancel route - NextRequest should be imported as a type since it's only used for type annotation.

-import { NextRequest, NextResponse } from "next/server";
+import { NextResponse, type NextRequest } from "next/server";
apps/web/app/api/admin/scheduled-actions/route.ts (1)

1-1: Fix import type usage for NextRequest.

NextRequest should be imported as a type since it's only used for type annotation.

-import { type NextRequest, NextResponse } from "next/server";
+import { NextResponse, type NextRequest } from "next/server";
apps/web/utils/scheduled-actions/executor.ts (1)

24-24: Great improvement on type safety!

The previous type safety concerns have been properly addressed:

  • Function parameter uses proper ScheduledAction type
  • No more as any type casting in the code

Also applies to: 216-223

🧹 Nitpick comments (3)
apps/web/utils/delayed-actions.ts (1)

1-16: LGTM! Well-structured utility for action delay validation.

The utility provides a clean, client-safe way to check action delay support. The choice of unsupported actions (MARK_SPAM, TRACK_THREAD, DIGEST) makes sense as these are typically immediate-effect actions.

Consider documenting why these specific actions don't support delays to help future maintainers understand the rationale.

apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts (1)

1-1: Fix import type usage for NextRequest.

NextRequest is only used as a type parameter, so it should be imported with import type to ensure it's removed during compilation.

-import { type NextRequest, NextResponse } from "next/server";
+import { NextResponse, type NextRequest } from "next/server";
apps/web/utils/scheduled-actions/scheduler.ts (1)

138-186: Consider persisting the cancellation reason in the database.

The reason parameter is only used for logging but not stored in the database. For better audit trails and debugging, consider adding a cancellationReason field to the ScheduledAction model.

Add the cancellation reason to the update data:

      data: {
        status: ScheduledActionStatus.CANCELLED,
+       cancellationReason: reason,
      },

This would require adding a cancellationReason field to the Prisma schema.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 728435a and 7d9811b.

📒 Files selected for processing (30)
  • apps/web/__tests__/ai-choose-rule.test.ts (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (10 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx (1 hunks)
  • apps/web/app/(app)/admin/page.tsx (2 hunks)
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx (1 hunks)
  • apps/web/app/(app)/admin/scheduled-actions/page.tsx (1 hunks)
  • apps/web/app/(app)/admin/scheduled-actions/types.ts (1 hunks)
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts (1 hunks)
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts (1 hunks)
  • apps/web/app/api/admin/scheduled-actions/route.ts (1 hunks)
  • apps/web/app/api/cron/scheduled-actions/route.ts (1 hunks)
  • apps/web/components/TooltipExplanation.tsx (2 hunks)
  • apps/web/prisma/migrations/20250625053006_add_delayed_actions/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (6 hunks)
  • apps/web/utils/action-item.ts (2 hunks)
  • apps/web/utils/actions/rule.ts (10 hunks)
  • apps/web/utils/actions/rule.validation.ts (3 hunks)
  • apps/web/utils/ai/assistant/chat.ts (4 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (3 hunks)
  • apps/web/utils/ai/rule/create-rule-schema.ts (2 hunks)
  • apps/web/utils/date.ts (1 hunks)
  • apps/web/utils/delayed-actions.ts (1 hunks)
  • apps/web/utils/reply-tracker/enable.ts (1 hunks)
  • apps/web/utils/rule/rule.ts (1 hunks)
  • apps/web/utils/schedule.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • apps/web/app/(app)/admin/page.tsx
  • apps/web/utils/reply-tracker/enable.ts
  • apps/web/tests/ai-choose-rule.test.ts
  • apps/web/app/(app)/admin/scheduled-actions/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/admin/scheduled-actions/types.ts
  • apps/web/utils/schedule.test.ts
  • apps/web/utils/date.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/utils/ai/choose-rule/run-rules.ts
  • apps/web/utils/scheduled-actions/scheduler.test.ts
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/prisma/schema.prisma
🧰 Additional context used
📓 Path-based instructions (17)
`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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/utils/rule/rule.ts
  • apps/web/utils/delayed-actions.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/components/TooltipExplanation.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/utils/scheduled-actions/executor.test.ts
  • apps/web/utils/ai/assistant/chat.ts
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/utils/actions/rule.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/utils/rule/rule.ts
  • apps/web/utils/delayed-actions.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/components/TooltipExplanation.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/utils/scheduled-actions/executor.test.ts
  • apps/web/utils/ai/assistant/chat.ts
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/utils/actions/rule.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/utils/rule/rule.ts
  • apps/web/utils/delayed-actions.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/components/TooltipExplanation.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/utils/scheduled-actions/executor.test.ts
  • apps/web/utils/ai/assistant/chat.ts
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/utils/actions/rule.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
  • apps/web/prisma/migrations/20250625053006_add_delayed_actions/migration.sql
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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]/assistant/onboarding/CategoriesSetup.tsx
  • apps/web/utils/rule/rule.ts
  • apps/web/utils/delayed-actions.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/components/TooltipExplanation.tsx
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/utils/scheduled-actions/executor.test.ts
  • apps/web/utils/ai/assistant/chat.ts
  • apps/web/app/(app)/admin/scheduled-actions/ScheduledActionsTable.tsx
  • apps/web/utils/actions/rule.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.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/rule/rule.ts
  • apps/web/utils/delayed-actions.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/utils/scheduled-actions/executor.test.ts
  • apps/web/utils/ai/assistant/chat.ts
  • apps/web/utils/actions/rule.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
`apps/web/utils/ai/**`: Main LLM implementations should be placed in this directory.

apps/web/utils/ai/**: Main LLM implementations should be placed in this directory.

  • apps/web/utils/ai/rule/create-rule-schema.ts
  • apps/web/utils/ai/assistant/chat.ts
`apps/web/**/components/**/*.{ts,tsx}`: Use PascalCase naming convention for component files. Prefer functional components with hooks. Use shadcn/ui components when available. Ensu...

apps/web/**/components/**/*.{ts,tsx}: Use PascalCase naming convention for component files.
Prefer functional components with hooks.
Use shadcn/ui components when available.
Ensure responsive design with a mobile-first approach.

  • apps/web/components/TooltipExplanation.tsx
`apps/web/components/**/*`: All other components are in components/.

apps/web/components/**/*: All other components are in components/.

  • apps/web/components/TooltipExplanation.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/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/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/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/app/api/admin/scheduled-actions/route.ts
  • apps/web/app/api/cron/scheduled-actions/route.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
`**/api/**/cron/**/*.ts`: Cron endpoints MUST use withError middleware and validate cron secret using hasCronSecret(request) or hasPostCronSecret(request). Cron endpoints MUST capt...

**/api/**/cron/**/*.ts: Cron endpoints MUST use withError middleware and validate cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return 401 status for unauthorized requests.
Cron endpoints MUST NOT use predictable or weak secrets for authentication; use strong, random secrets stored in environment variables.

  • apps/web/app/api/cron/scheduled-actions/route.ts
`**/*.test.ts`: Tests should be colocated next to the tested file, following the pattern: 'dir/format.ts' and 'dir/format.test.ts'.

**/*.test.ts: Tests should be colocated next to the tested file, following the pattern: 'dir/format.ts' and 'dir/format.test.ts'.

  • apps/web/utils/scheduled-actions/executor.test.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/rule.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/rule.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/rule.ts
🪛 Biome (1.9.4)
apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts

[error] 1-1: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

🔇 Additional comments (27)
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx (1)

210-212: LGTM! Clean addition of delayed archive option.

The new "Label + Archive after a week" option follows the existing SelectItem pattern and provides a clear, user-friendly description of the delayed action functionality.

apps/web/utils/rule/rule.ts (1)

350-350: LGTM! Consistent field mapping for delay support.

The addition of delayInMinutes to the action field mapping follows the existing pattern and properly handles the optional nature of the field.

apps/web/utils/ai/rule/create-rule-schema.ts (2)

7-7: LGTM! Proper import for validation constant.

Good practice importing the NINETY_DAYS_MINUTES constant for consistent validation limits across the application.


80-87: LGTM! Well-structured delay field validation.

The delayInMinutes field is properly implemented with:

  • Appropriate type and constraints (1 minute to 90 days)
  • Nullish for optional nature
  • Clear, descriptive validation messages
  • Helpful field description

The validation range strikes a good balance between flexibility and preventing abuse.

apps/web/utils/action-item.ts (2)

165-174: LGTM! Consistent type extension for delay support.

The ActionFieldsSelection type properly includes delayInMinutes alongside existing action fields, maintaining consistency with the existing pattern.


179-189: LGTM! Proper initialization of delay field.

The delayInMinutes field is correctly initialized in the base object using the same null-coalescing pattern as other optional fields, ensuring consistent handling throughout the sanitization process.

apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts (1)

7-58: LGTM! Solid admin API implementation with proper security checks.

The route correctly implements:

  • Session authentication and admin authorization
  • Resource existence validation
  • Business logic validation (only PENDING actions can be cancelled)
  • Proper status transition and error handling
apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts (1)

7-58: LGTM! Consistent implementation with proper retry logic.

The route correctly:

  • Validates that only FAILED actions can be retried
  • Transitions status from FAILED back to PENDING for reprocessing
  • Follows the same secure patterns as the cancel route
apps/web/app/api/cron/scheduled-actions/route.ts (1)

15-74: LGTM! Excellent cron job implementation with proper safeguards.

The implementation correctly follows all coding guidelines:

  • Uses withError middleware and hasPostCronSecret for authentication
  • Implements proper concurrency control by marking actions as executing
  • Isolates errors so individual failures don't stop batch processing
  • Includes comprehensive logging for monitoring and debugging
  • Has appropriate timeout (5 minutes) for long-running operations
apps/web/components/TooltipExplanation.tsx (1)

4-52: LGTM! Excellent refactor to controlled tooltip system.

The refactor effectively addresses modal tooltip positioning issues by:

  • Adding controlled state for manual tooltip visibility management
  • Introducing configurable side positioning prop
  • Implementing onClick toggle for better user control in complex UI contexts

The implementation follows React best practices and maintains type safety.

apps/web/app/api/admin/scheduled-actions/route.ts (1)

6-148: LGTM! Comprehensive admin API with excellent filtering and performance optimization.

The implementation excels in:

  • Flexible filtering system (email, status, rule name search)
  • Performance optimization with concurrent Promise.all queries
  • Proper data aggregation for status counts and totals
  • Reasonable result limiting (100 items) to prevent performance issues
  • Comprehensive error handling and proper admin authorization
apps/web/utils/ai/assistant/chat.ts (1)

18-18: Well-implemented delay feature integration!

The addition of delayInMinutes field is properly implemented with:

  • Appropriate validation constraints (1 minute to 90 days)
  • Clear description for API consumers
  • Consistent type definitions
  • Correct handling in the execution flow

Also applies to: 95-103, 126-126, 766-766

apps/web/utils/scheduled-actions/executor.test.ts (1)

1-274: Excellent test coverage for the executor module!

The test suite comprehensively covers:

  • Successful execution with proper state transitions
  • Error handling with failed status updates
  • Edge case of missing email account

All dependencies are properly mocked and assertions validate the expected behavior.

apps/web/utils/actions/rule.ts (1)

48-48: Solid implementation of delayed actions in rule management!

The changes properly integrate the delay feature:

  • Correctly handles delayInMinutes in both create and update operations
  • Implements the new "label_archive_1_week" onboarding option consistently
  • Updates user-facing descriptions appropriately

Also applies to: 80-101, 232-232, 251-251, 483-484, 559-566, 598-605, 622-627

apps/web/utils/scheduled-actions/executor.ts (1)

255-267: I didn’t find the ScheduledAction model via ast-grep. Let’s locate the Prisma schema file and print the ScheduledAction block to check for an error field:

#!/bin/bash
# Locate a Prisma schema file
schema_file=$(fd --extension prisma | head -n1)
echo "Using schema file: $schema_file"

# Print the ScheduledAction model definition
sed -n '/model ScheduledAction {/,/}/p' "$schema_file"
apps/web/utils/scheduled-actions/scheduler.ts (4)

1-78: LGTM! Well-structured scheduler service with comprehensive error handling.

The createScheduledAction function properly validates action types, stores all necessary ActionItem data, and includes appropriate error handling with logging.


80-136: Function correctly handles scheduling of delayed actions.

The non-null assertion on line 113 is safe due to the filtering logic that ensures delayInMinutes is not null. The time calculation properly adds the delay to the email's internal date.


188-219: Efficient query for retrieving due scheduled actions.

The function properly filters for PENDING actions that are due, includes necessary relations, and implements sensible ordering and limiting.


221-245: Good implementation of optimistic locking for concurrent execution prevention.

The function correctly uses the status field in the WHERE clause to prevent race conditions when multiple workers might try to execute the same action.

apps/web/prisma/migrations/20250625053006_add_delayed_actions/migration.sql (2)

1-21: Verify the intentional removal of digestScheduleId column.

The migration will drop the digestScheduleId column from the EmailAccount table, resulting in data loss. Please confirm this is intentional and that any existing digest schedule data has been migrated or is no longer needed.


22-63: Well-designed ScheduledAction table with appropriate indexes.

The table structure includes all necessary fields for delayed action execution, and the indexes are optimized for the expected query patterns (finding due actions and querying by email).

apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (6)

94-94: LGTM! Appropriate import for delayed action support.


129-142: Correctly preserves delayInMinutes when loading existing rules.


860-864: Appropriate default value for new actions.

Setting delayInMinutes: null ensures new actions don't have delays by default, which is the expected behavior.


1082-1139: Well-structured UI for delay configuration.

The toggle controls are properly organized and only shown for relevant action types. The tooltip provides clear guidance about the delay feature.


1481-1582: Excellent implementation of delay input with unit conversion.

The DelayInputControls component properly handles conversion between minutes, hours, and days, with correct calculations and good UX.


1286-1297: ```shell
#!/bin/bash
echo "=== create-rule-schema.ts around delayInMinutes ==="
rg -n 'delayInMinutes' -A 5 apps/web/utils/ai/rule/create-rule-schema.ts

echo "=== rule.validation.ts around delayInMinutes ==="
rg -n 'delayInMinutes' -A 5 apps/web/utils/actions/rule.validation.ts


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

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: 2

🧹 Nitpick comments (1)
apps/web/components/ui/tooltip.tsx (1)

16-27: Excellent portal implementation with one consideration.

The portal wrapping is a best practice that prevents z-index and overflow issues. However, the z-index value of 9999 is extremely high and might cause issues if other elements need to appear above tooltips.

Consider using a more reasonable z-index value like z-[100] or z-[200] unless there's a specific requirement for such a high value.

-        "z-[9999] overflow-hidden rounded-md border border-slate-700 bg-gray-900 px-3 py-1.5 text-sm text-slate-50 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-100 dark:bg-slate-50 dark:text-slate-950",
+        "z-[100] overflow-hidden rounded-md border border-slate-700 bg-gray-900 px-3 py-1.5 text-sm text-slate-50 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-100 dark:bg-slate-50 dark:text-slate-950",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 300d336 and 827c424.

📒 Files selected for processing (12)
  • .cursor/rules/features/delayed-actions.mdc (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (10 hunks)
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts (1 hunks)
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts (1 hunks)
  • apps/web/app/api/scheduled-actions/execute/route.ts (1 hunks)
  • apps/web/components/ui/tooltip.tsx (1 hunks)
  • apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (6 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (4 hunks)
  • apps/web/utils/scheduled-actions/executor.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/utils/ai/choose-rule/run-rules.ts
  • apps/web/utils/scheduled-actions/scheduler.test.ts
  • apps/web/app/api/admin/scheduled-actions/[id]/cancel/route.ts
  • apps/web/utils/scheduled-actions/executor.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/prisma/schema.prisma
  • apps/web/app/api/admin/scheduled-actions/[id]/retry/route.ts
🧰 Additional context used
📓 Path-based instructions (11)
`apps/web/**/*.{ts,tsx}`: Use TypeScript with strict null checks enabled. Use pa...

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

📄 Source: CodeRabbit Inference Engine (apps/web/CLAUDE.md)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
  • apps/web/app/api/scheduled-actions/execute/route.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
`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.

📄 Source: CodeRabbit Inference Engine (apps/web/CLAUDE.md)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
  • apps/web/app/api/scheduled-actions/execute/route.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
`apps/web/**/components/**/*.{ts,tsx}`: Use PascalCase naming convention for com...

apps/web/**/components/**/*.{ts,tsx}: Use PascalCase naming convention for component files.
Prefer functional components with hooks.
Use shadcn/ui components when available.
Ensure responsive design with a mobile-first approach.

📄 Source: CodeRabbit Inference Engine (apps/web/CLAUDE.md)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.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.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/installing-packages.mdc)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
  • apps/web/app/api/scheduled-actions/execute/route.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql
`apps/web/components/ui/**/*`: Shadcn components are in components/ui.

apps/web/components/ui/**/*: Shadcn components are in components/ui.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
`apps/web/components/**/*`: All other components are in components/.

apps/web/components/**/*: All other components are in components/.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
`**/*.{js,jsx,ts,tsx}`: Use Shadcn UI and Tailwind for components and styling. I...

**/*.{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.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui-components.mdc)

List of files the instruction was applied to:

  • apps/web/components/ui/tooltip.tsx
  • apps/web/app/api/scheduled-actions/execute/route.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
`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.

📄 Source: CodeRabbit Inference Engine (apps/web/CLAUDE.md)

List of files the instruction was applied to:

  • apps/web/app/api/scheduled-actions/execute/route.ts
`apps/web/app/api/**/*`: All API route handlers must use authentication middlewa...

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.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)

List of files the instruction was applied to:

  • apps/web/app/api/scheduled-actions/execute/route.ts
`**/api/**/*.ts`: ALL API routes that handle user data MUST use appropriate auth...

**/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.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/security.mdc)

List of files the instruction was applied to:

  • apps/web/app/api/scheduled-actions/execute/route.ts
`apps/web/utils/**/*`: Create utility functions in utils/ folder for reusable logic.

apps/web/utils/**/*: Create utility functions in utils/ folder for reusable logic.

📄 Source: CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

List of files the instruction was applied to:

  • apps/web/utils/scheduled-actions/scheduler.ts
🧬 Code Graph Analysis (2)
apps/web/components/ui/tooltip.tsx (1)
apps/web/utils/index.ts (1)
  • cn (4-6)
apps/web/utils/scheduled-actions/scheduler.ts (4)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/ai/types.ts (1)
  • ActionItem (15-26)
apps/web/utils/delayed-actions.ts (1)
  • canActionBeDelayed (14-16)
apps/web/utils/cron.ts (1)
  • getCronSecretHeader (26-28)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Secret Detection
  • GitHub Check: Jit Security
🔇 Additional comments (19)
apps/web/components/ui/tooltip.tsx (4)

11-11: LGTM! Good addition for flexibility.

Adding TooltipPortal as a separate export provides flexibility for cases where direct portal control is needed.


13-13: Good TypeScript improvement.

The change from React.ElementRef to React.ComponentRef is the correct modern TypeScript pattern for component refs.


22-22: Verify the dark theme styling is intentional.

The tooltip styling has changed from a light default theme to a dark theme (gray-900 background). This is a significant visual change that will affect the appearance across the application.

Please confirm this dark theme styling is intentional and aligns with the overall design system.


31-37: LGTM! Clean export structure.

The export structure is well-organized and includes the new TooltipPortal component appropriately.

apps/web/app/api/scheduled-actions/execute/route.ts (3)

42-48: Database query appears secure.

The query properly filters by the scheduled action ID and includes necessary relations. No user input is directly used in the query, which prevents injection attacks.


100-103: Comprehensive error handling implemented.

The try-catch block properly handles unexpected errors and provides appropriate logging and HTTP responses.


74-76: Verify atomic operation behavior.

The markQStashActionAsExecuting function should use an atomic update with a WHERE condition to prevent race conditions.

#!/bin/bash
# Check the implementation of markQStashActionAsExecuting for atomic updates
ast-grep --pattern 'export async function markQStashActionAsExecuting($$$) {
  $$$
}'
.cursor/rules/features/delayed-actions.mdc (1)

1-151: Comprehensive and well-structured feature documentation.

The documentation effectively covers:

  • Architecture overview and use cases
  • Database schema design
  • QStash integration details
  • API endpoints and error handling
  • Benefits over alternative approaches

The code examples are accurate and the explanations are clear for developers implementing or maintaining this feature.

apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql (4)

8-8: Well-designed status enum for action lifecycle.

The ScheduledActionStatus enum covers all necessary states: PENDING, EXECUTING, COMPLETED, FAILED, CANCELLED. This provides clear status tracking throughout the action lifecycle.


23-46: Comprehensive table design with proper field coverage.

The ScheduledAction table includes:

  • All necessary action metadata (type, timing, status)
  • Email context (messageId, threadId, emailAccountId)
  • Action-specific fields (label, subject, content, etc.)
  • QStash integration (qstashMessageId)
  • Execution tracking (executedAt, executedActionId)

The design supports all documented action types and use cases.


48-58: Efficient indexing strategy implemented.

The indexes are well-chosen for the expected query patterns:

  • status_scheduledFor_idx: For finding pending actions to execute
  • emailAccountId_messageId_idx: For canceling actions by email
  • qstashMessageId_idx: For QStash message management

This should provide good performance for the scheduler operations.


60-67: Proper foreign key relationships with appropriate cascade behavior.

The foreign key constraints ensure data integrity:

  • executedRuleIdExecutedRule with CASCADE DELETE (appropriate for audit trail)
  • executedActionIdExecutedAction with SET NULL (allows independent lifecycle)
  • emailAccountIdEmailAccount with CASCADE DELETE (proper cleanup)
apps/web/utils/scheduled-actions/scheduler.ts (7)

27-30: Proper environment-based QStash client initialization.

The client factory function correctly handles the case where QStash token is not available, returning null for graceful degradation.


50-54: Essential validation for action type support.

The function properly validates that the action type supports delayed execution before proceeding, preventing invalid scheduled actions.


58-76: Comprehensive database record creation.

The scheduled action creation includes all necessary fields from the ActionItem and properly maps the data structure for storage.


266-286: Robust error handling for QStash cancellation.

The code properly handles QStash cancellation failures without failing the entire operation, which is appropriate since database state is the source of truth.


334-334: Environment variable fallback strategy.

The URL construction properly falls back from WEBHOOK_URL to NEXT_PUBLIC_BASE_URL, providing flexibility for different deployment environments.


340-348: Secure QStash message publishing configuration.

The QStash publishing configuration includes proper security headers and deduplication settings:

  • Uses cron secret headers for authentication
  • Disables content-based deduplication in favor of explicit IDs
  • Uses absolute timestamps for precise scheduling

407-415: Atomic database operation for status transitions.

The markQStashActionAsExecuting function uses a proper WHERE condition with the PENDING status, ensuring atomic updates and preventing race conditions.

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: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 922e979 and b762db6.

📒 Files selected for processing (8)
  • apps/web/app/(app)/[emailAccountId]/assistant/DraftReplies.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (18 hunks)
  • apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (6 hunks)
  • apps/web/utils/action-item.ts (2 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (4 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/utils/ai/choose-rule/run-rules.ts
  • apps/web/utils/action-item.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/prisma/schema.prisma
  • apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql
🧰 Additional context used
📓 Path-based instructions (5)
apps/web/**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • apps/web/CLAUDE.md
apps/web/app/**/*

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • apps/web/CLAUDE.md
apps/web/**/*.tsx

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • apps/web/CLAUDE.md
**/*.tsx

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/data-fetching.mdc
  • .cursor/rules/form-handling.mdc
**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/form-handling.mdc
🧠 Learnings (2)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (6)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: For mutating data, use Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Validate form inputs before submission
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: To handle loading and error states in UI components, wrap content with a `LoadingContent` component that takes `loading` and `error` props, and only renders the main content when data is available.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (18)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: For form text inputs in React using Shadcn UI, use the `Input` component with `registerProps` from a form library (such as react-hook-form) and pass any validation errors to the `error` prop.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Validate form inputs before submission
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use shadcn/ui components when available
Learnt from: aryanprince
PR: elie222/inbox-zero#210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: For multi-line text input, use the `Input` component with `type="text"`, `autosizeTextarea`, and `rows` props to create an autosizing textarea, and handle validation and errors similarly to single-line inputs.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Show validation errors inline next to form fields
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Input validation schemas for server actions should be defined using Zod in dedicated .validation.ts files, which can be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Use descriptive error messages
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-06-23T12:27:17.978Z
Learning: Error responses should use generic messages and consistent formats (e.g., SafeError) to avoid leaking sensitive information or internal logic.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-06-23T12:27:05.686Z
Learning: Error messages returned to clients should not reveal internal details or sensitive data; use generic error messages and SafeError for consistent and secure error responses.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Each custom React hook should have a single responsibility and be kept as simple as possible.
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Custom React hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: For mutating data, use Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: For data fetching in custom React hooks, prefer using the useSWR hook. The custom hook should typically wrap useSWR, handle the API endpoint URL, and return the data, loading state, error state, and potentially the mutate function from SWR.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Server actions must only be used for mutations (create, update, delete operations) and never for data fetching (GET operations); data fetching should be handled via dedicated GET API routes and SWR hooks.
🧬 Code Graph Analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
apps/web/utils/delayed-actions.ts (1)
  • canActionBeDelayed (15-17)
⏰ 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: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (18)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (2)

31-31: LGTM - Proper extraction of mutate function

The addition of mutate to the destructuring is correct and follows the established pattern for SWR data mutations.


55-55: LGTM - Proper prop passing for optimistic updates

Passing the mutate function to RuleForm enables optimistic UI updates for the delayed actions feature, which is essential for a smooth user experience.

apps/web/app/(app)/[emailAccountId]/assistant/DraftReplies.tsx (1)

95-95: LGTM - Proper schema alignment for delayed actions

Adding delayInMinutes: null to the optimistic update object ensures consistency with the updated action schema that supports delayed execution. The null value correctly represents no delay by default.

apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (15)

94-94: LGTM - Proper import for delay validation

The import of canActionBeDelayed utility function is correctly added to support the delayed actions feature.


103-103: LGTM - Proper mutate prop handling

The addition of mutate to the useRule hook destructuring and passing it to RuleForm enables optimistic updates for the delayed actions feature.

Also applies to: 107-114


123-123: LGTM - Proper prop definition for mutate function

The mutate prop is correctly typed as optional and properly defined in the component interface.

Also applies to: 129-129


142-142: LGTM - Proper delay field initialization

The delayInMinutes field is correctly included in the form default values, maintaining the existing value from the rule data.


209-232: LGTM - Proper optimistic update implementation

The optimistic update logic for delayInMinutes is well-implemented. It updates the delay state immediately to prevent stale data when the modal is reopened, then properly handles revalidation on both success and error cases.


286-296: LGTM - Proper dependency array update

The dependency array for the onSubmit callback is correctly updated to include the new mutate and rule dependencies.


335-335: LGTM - Proper action type ordering

Moving the "Digest" action to appear after "Mark spam" improves the logical grouping of action types in the UI.


877-877: LGTM - Proper prop renaming for clarity

Renaming the prop from mutate to mutateLabels improves code clarity by distinguishing between different mutation functions.


908-923: LGTM - Improved tooltip positioning

Adding side="left" to the tooltip improves the user experience by ensuring proper positioning relative to the toggle control.


1038-1044: LGTM - Proper delay capability check

The useMemo hook correctly computes whether an action can be delayed based on its type, with proper dependency tracking.


1147-1150: LGTM - Consistent tooltip positioning

Adding side="left" to the tooltip maintains consistent positioning throughout the UI.


1288-1328: LGTM - Well-implemented delay controls

The delay controls are properly implemented with:

  • Conditional rendering based on action capability
  • Proper error handling and validation
  • Clear tooltip explanation
  • Intuitive toggle functionality

The implementation follows React best practices and integrates well with the existing form structure.


1437-1437: LGTM - Consistent tooltip positioning

Adding side="left" to the ThreadsExplanation tooltip maintains UI consistency.


1499-1564: LGTM - Well-implemented delay input controls

The DelayInputControls component is properly implemented with:

  • Correct time unit conversion logic
  • Proper form integration with setValue
  • Input validation and sanitization
  • Clear UI with appropriate styling

The implementation follows React Hook Form patterns and provides a good user experience.


1566-1596: LGTM - Correct time conversion utilities

The helper functions getDisplayValueAndUnit and convertToMinutes are correctly implemented:

  • Proper handling of null/undefined values
  • Correct time unit conversions (minutes, hours, days)
  • Appropriate fallback values
  • Clear separation of concerns

The logic properly converts between internal storage format (minutes) and user-friendly display format.

@edulelis
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

✅ Actions performed

Full review triggered.

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: 4

♻️ Duplicate comments (2)
apps/web/utils/delayed-actions.ts (1)

1-18: Implementation looks good, but consider past review feedback.

The centralized approach for defining delay-supported actions is solid and follows good practices. The function is simple and the logic is clear.

However, based on past review comments, consider:

  1. The naming suggestion to change to "SUPPORTED_DELAYED_ACTIONS" (which appears to already be implemented)
  2. The concern about permissiveness - though the current implementation is actually restrictive since new actions must be explicitly added to the array

The explicit allowlist approach is secure and maintainable.

apps/web/components/ui/tooltip.tsx (1)

37-59: TooltipContent implementation addresses past review feedback.

The use of TooltipPrimitive.Portal aligns with the past review comment about using the latest shadcn code with portal support. The styling update to primary colors and the explicit arrow element improve the visual consistency.

However, consider verifying this implementation against the latest shadcn tooltip component to ensure full compatibility:

What is the current shadcn/ui tooltip component implementation and does it match this code structure?
🧹 Nitpick comments (9)
apps/web/utils/schedule.test.ts (1)

330-334: Spec title is misleading

The test name says “on same day if time has passed”, yet the expected date is the 22nd (next Monday), not the 15th. Rename the spec (e.g. “should schedule for next week when time has passed”) or adjust the expectation for clarity.

apps/web/__tests__/ai-choose-rule.test.ts (1)

65-81: Consider creating a helper function for action objects.

While the delayInMinutes addition is correct, the inline action object creation could benefit from a helper function to ensure consistency and reduce duplication across tests.

Consider creating a helper function like:

function getAction(overrides: Partial<Action> = {}): Action {
  return {
    id: "id",
    createdAt: new Date(),
    updatedAt: new Date(),
    type: ActionType.REPLY,
    ruleId: "ruleId",
    label: null,
    subject: null,
    content: "{{Write a joke}}",
    to: null,
    cc: null,
    bcc: null,
    url: null,
    delayInMinutes: null,
    ...overrides,
  };
}

This would align with the learning about using helper functions for common test data.

apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)

155-170: Consider adding delayInMinutes to DIGEST action for schema consistency.

While the DIGEST action type may not support delays (based on the delayed-actions.ts utility), adding delayInMinutes: null would ensure schema consistency across all action objects.

Consider adding the field for consistency:

  emailAccountData?.coldEmailDigest
    ? {
        id: "cold-email-blocker-digest",
        type: ActionType.DIGEST,
        label: null,
        createdAt: new Date(),
        updatedAt: new Date(),
        ruleId: COLD_EMAIL_BLOCKER_RULE_ID,
        to: null,
        subject: null,
        content: null,
        cc: null,
        bcc: null,
        url: null,
+       delayInMinutes: null,
      }
    : null,
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)

55-55: Consider consistency: mutate prop missing for create mode.

While the mutate prop is correctly passed to RuleForm in edit mode, the create mode RuleForm (lines 60-73) doesn't receive this prop. This could lead to inconsistent behavior between create and edit modes.

            <RuleForm
              rule={{
                name: "",
                actions: [],
                conditions: [],
                automate: true,
                runOnThreads: true,
                conditionalOperator: "AND" as const,
                ...initialRule,
              }}
              alwaysEditMode={true}
              onSuccess={handleSuccess}
              isDialog={true}
+             mutate={mutate}
            />
apps/web/utils/scheduled-actions/scheduler.test.ts (1)

44-47: Create helper functions for test data.

Following the project's testing best practices, create helper functions to reduce duplication and improve type safety.

Add a helper function at the top of the test file:

function createMockScheduledAction(overrides?: Partial<any>) {
  return {
    id: "action-1",
    scheduledId: "qstash-msg-1",
    ...overrides,
  };
}

Then use it in tests:

-      prisma.scheduledAction.findMany.mockResolvedValue([
-        { id: "action-1", scheduledId: "qstash-msg-1" },
-        { id: "action-2", scheduledId: "qstash-msg-2" },
-      ] as any);
+      prisma.scheduledAction.findMany.mockResolvedValue([
+        createMockScheduledAction(),
+        createMockScheduledAction({ id: "action-2", scheduledId: "qstash-msg-2" }),
+      ]);

Also applies to: 96-98

apps/web/utils/scheduled-actions/executor.test.ts (2)

35-55: Extract mock data to a helper function.

Following testing best practices, create a helper function for the mock scheduled action data.

function createMockScheduledAction(overrides: Partial<any> = {}) {
  return {
    id: "scheduled-action-123",
    executedRuleId: "rule-123",
    actionType: ActionType.ARCHIVE,
    messageId: "msg-123",
    threadId: "thread-123",
    emailAccountId: "account-123",
    scheduledFor: new Date("2024-01-01T12:00:00Z"),
    status: ScheduledActionStatus.PENDING,
    label: null,
    subject: null,
    content: null,
    to: null,
    cc: null,
    bcc: null,
    url: null,
    createdAt: new Date(),
    updatedAt: new Date(),
    executedAt: null,
    executedActionId: null,
    ...overrides,
  };
}

Then use it in each test:

-    const mockScheduledAction = {
-      id: "scheduled-action-123",
-      // ... all fields
-    } as any;
+    const mockScheduledAction = createMockScheduledAction();

104-149: Extract common mock setup to reduce duplication.

The mock setup for parseMessage, getEmailAccountWithAiAndTokens, and other utilities is duplicated across tests.

Create setup helper functions:

function setupCommonMocks() {
  const { runActionFunction } = vi.hoisted(() => ({ 
    runActionFunction: vi.fn() 
  }));
  
  const { parseMessage } = vi.hoisted(() => ({
    parseMessage: vi.fn().mockReturnValue({
      id: "msg-123",
      threadId: "thread-123",
      headers: {},
      textPlain: "test content",
      textHtml: "<p>test content</p>",
      attachments: [],
      internalDate: "1234567890",
      snippet: "",
      historyId: "",
      inline: [],
    })
  }));
  
  // ... other common mocks
  
  return { runActionFunction, parseMessage, /* ... */ };
}

This would significantly reduce the test file size and improve maintainability.

Also applies to: 198-245

apps/web/utils/scheduled-actions/executor.ts (1)

200-213: Consider using a more specific type instead of ParsedMessage.

The hardcoded empty values for snippet, historyId, and inline suggest that ParsedMessage contains fields unnecessary for action execution. Consider creating a more specific interface or using TypeScript's Pick utility type to only include the required fields.

-  // Create a ParsedMessage from EmailForAction
-  const parsedMessage: ParsedMessage = {
-    id: emailMessage.id,
-    threadId: emailMessage.threadId,
-    headers: emailMessage.headers,
-    textPlain: emailMessage.textPlain,
-    textHtml: emailMessage.textHtml,
-    attachments: emailMessage.attachments,
-    internalDate: emailMessage.internalDate,
-    // Required ParsedMessage fields that aren't used in action execution
-    snippet: "",
-    historyId: "",
-    inline: [],
-  };
+  // Use only the fields required for action execution
+  type EmailForExecution = Pick<ParsedMessage, 
+    'id' | 'threadId' | 'headers' | 'textPlain' | 'textHtml' | 'attachments' | 'internalDate'
+  >;
+  
+  const emailForExecution: EmailForExecution = {
+    id: emailMessage.id,
+    threadId: emailMessage.threadId,
+    headers: emailMessage.headers,
+    textPlain: emailMessage.textPlain,
+    textHtml: emailMessage.textHtml,
+    attachments: emailMessage.attachments,
+    internalDate: emailMessage.internalDate,
+  };

Then update runActionFunction to accept this more specific type.

apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

1565-1595: Consider using null instead of -1 for invalid values

The time conversion functions are well-implemented. However, consider returning null instead of -1 for invalid values to be more explicit about the absence of a valid delay:

-  if (minutes === -1 || minutes <= 0) return { value: "", unit: "hours" };
+  if (minutes === null || minutes <= 0) return { value: "", unit: "hours" };
-  if (Number.isNaN(numValue) || numValue <= 0) return -1;
+  if (Number.isNaN(numValue) || numValue <= 0) return null;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f650225 and facf647.

📒 Files selected for processing (27)
  • .cursor/rules/features/delayed-actions.mdc (1 hunks)
  • apps/web/__tests__/ai-choose-rule.test.ts (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/DraftReplies.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (18 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx (1 hunks)
  • apps/web/app/api/scheduled-actions/execute/route.ts (1 hunks)
  • apps/web/components/TooltipExplanation.tsx (2 hunks)
  • apps/web/components/ui/tooltip.tsx (1 hunks)
  • apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (6 hunks)
  • apps/web/utils/action-item.ts (2 hunks)
  • apps/web/utils/actions/rule.ts (10 hunks)
  • apps/web/utils/actions/rule.validation.ts (3 hunks)
  • apps/web/utils/ai/assistant/chat.ts (4 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (4 hunks)
  • apps/web/utils/ai/rule/create-rule-schema.ts (2 hunks)
  • apps/web/utils/ai/types.ts (1 hunks)
  • apps/web/utils/date.ts (1 hunks)
  • apps/web/utils/delayed-actions.ts (1 hunks)
  • apps/web/utils/rule/rule.ts (1 hunks)
  • apps/web/utils/schedule.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/executor.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (17)
apps/web/utils/schedule.test.ts (1)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-06-23T12:26:27.693Z
Learning: When writing tests for LLM-related functionality in TypeScript (e.g., in 'apps/web/__tests__/*.test.ts'), always create helper functions for common test data to ensure consistency and reduce duplication.
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (5)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: For mutating data, use Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Validate form inputs before submission
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: To handle loading and error states in UI components, wrap content with a `LoadingContent` component that takes `loading` and `error` props, and only renders the main content when data is available.
apps/web/__tests__/ai-choose-rule.test.ts (1)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-06-23T12:26:27.693Z
Learning: When writing tests for LLM-related functionality in TypeScript (e.g., in 'apps/web/__tests__/*.test.ts'), always create helper functions for common test data to ensure consistency and reduce duplication.
apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx (6)
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/cursor-rules.mdc:0-0
Timestamp: 2025-07-03T12:02:24.598Z
Learning: Applies to .cursor/rules/[a-z0-9\-]*.mdc : Use kebab-case for filenames of Cursor rule files
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/cursor-rules.mdc:0-0
Timestamp: 2025-07-03T12:02:24.598Z
Learning: Applies to .cursor/rules/*.mdc : Make Cursor rule file names descriptive of the rule's purpose
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Consult .cursor/rules for environment variable management
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-06-23T12:26:51.119Z
Learning: When implementing feature flags using PostHog in a TypeScript React codebase, always use kebab-case for flag keys (e.g., 'inbox-cleaner', 'pricing-options-2') to maintain consistency.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Follow consistent naming conventions (PascalCase for components)
apps/web/utils/ai/choose-rule/run-rules.ts (3)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-06-23T12:27:35.927Z
Learning: Import specific lodash functions (e.g., 'import groupBy from "lodash/groupBy";') instead of the entire library to minimize bundle size.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-06-23T12:26:59.468Z
Learning: When importing lodash utilities in TypeScript/JavaScript projects, import specific functions (e.g., import groupBy from 'lodash/groupBy') to minimize bundle size.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-06-23T12:27:35.927Z
Learning: Use lodash utilities for common operations involving arrays, objects, and strings to promote code consistency and reliability.
apps/web/utils/delayed-actions.ts (1)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/types/**/*.{ts,d.ts} : Centralize types in dedicated type files when shared
apps/web/utils/scheduled-actions/scheduler.test.ts (3)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-06-23T12:26:27.693Z
Learning: When writing tests for LLM-related functionality in TypeScript (e.g., in 'apps/web/__tests__/*.test.ts'), always create helper functions for common test data to ensure consistency and reduce duplication.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When using Vitest for testing, each test should be independent, use descriptive test names, mock external dependencies, and clean up mocks between tests.
apps/web/utils/ai/assistant/chat.ts (1)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-06-23T12:26:36.694Z
Learning: When implementing LLM-related functions in TypeScript, follow a standard structure: create a scoped logger, define a Zod schema for output validation, perform early input validation and returns, separate system and user prompts, log inputs and outputs, call the LLM with proper configuration, and return the validated result.
apps/web/components/TooltipExplanation.tsx (4)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use shadcn/ui components when available
Learnt from: aryanprince
PR: elie222/inbox-zero#210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-06-23T12:26:59.468Z
Learning: For components with onClick handlers in Next.js App Router, ensure they are client components by including the 'use client' directive at the top of the file.
apps/web/utils/actions/rule.ts (1)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-06-23T12:26:51.119Z
Learning: For A/B test feature flags, always define a TypeScript type for the possible variants and ensure the hook returns a default fallback value (such as 'control') if the flag is not set.
apps/web/utils/actions/rule.validation.ts (3)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Input validation schemas for server actions should be defined using Zod in dedicated .validation.ts files, which can be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Define validation schemas using Zod
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
apps/web/utils/scheduled-actions/executor.ts (8)
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/api/user/stats/newsletters/route.ts:235-239
Timestamp: 2025-07-08T13:14:03.250Z
Learning: The user prefers proper TypeScript error type checking (using `error instanceof Error`) over type assertions with `as any` for better type safety and code quality.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-06-23T12:26:36.694Z
Learning: Use TypeScript types for all parameters and return values, and define clear interfaces for complex input and output structures to ensure type safety.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Use descriptive error messages
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Use proper error handling with try/catch blocks
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-06-23T12:26:27.693Z
Learning: When writing tests for LLM-related functionality in TypeScript (e.g., in 'apps/web/__tests__/*.test.ts'), always create helper functions for common test data to ensure consistency and reduce duplication.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-06-23T12:26:14.189Z
Learning: Always use wrapper functions from the utils folder (e.g., apps/web/utils/gmail/message.ts) to interact with provider APIs, ensuring maintainability and easier future provider support.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: In Next.js projects, server actions should be implemented using the next-safe-action library to ensure type safety, input validation, context management, and centralized error handling.
apps/web/app/api/scheduled-actions/execute/route.ts (19)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-06-23T12:27:05.686Z
Learning: Cron endpoints in API routes must be protected with secret validation middleware such as hasCronSecret or hasPostCronSecret to prevent unauthorized access.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-06-23T12:27:17.978Z
Learning: All input parameters (route, query, body) must be validated for type, format, and length before use; use libraries like Zod for request body validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Input validation schemas for server actions should be defined using Zod in dedicated .validation.ts files, which can be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Define validation schemas using Zod
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-06-23T12:27:05.686Z
Learning: Never use request parameters directly in database queries; always validate parameters (e.g., check type and presence) before use, and prefer using Zod schemas for request body validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: In Next.js projects, server actions should be implemented using the next-safe-action library to ensure type safety, input validation, context management, and centralized error handling.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-06-23T12:26:36.694Z
Learning: When implementing LLM-related functions in TypeScript, follow a standard structure: create a scoped logger, define a Zod schema for output validation, perform early input validation and returns, separate system and user prompts, log inputs and outputs, call the LLM with proper configuration, and return the validated result.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: The project uses PostgreSQL as the database with Prisma.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-06-23T12:26:11.276Z
Learning: In Next.js App Router, GET API route handlers should be wrapped with either `withAuth` or `withEmailAccount` middleware to ensure consistent error handling and authentication.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-06-23T12:27:05.686Z
Learning: In Next.js API routes under apps/web/app/api/, always use authentication middleware such as withAuth or withEmailAccount, or ensure custom authentication logic is present if using withError.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: For mutating data, use Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Server actions must only be used for mutations (create, update, delete operations) and never for data fetching (GET operations); data fetching should be handled via dedicated GET API routes and SWR hooks.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-06-23T12:27:17.978Z
Learning: In Next.js/TypeScript API routes, always use authentication middleware (withAuth for user-level, withEmailAccount for account-level) to protect endpoints that handle user data.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/app/**/* : NextJS app router structure with (app) directory
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-06-23T12:27:17.978Z
Learning: Cron endpoints must use withError middleware and validate requests with strong secrets (hasCronSecret/hasPostCronSecret); never use weak or predictable secrets.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-06-23T12:26:11.276Z
Learning: Responses from GET API routes in Next.js should be returned using `NextResponse.json()` for consistency and proper response formatting.
apps/web/utils/ai/rule/create-rule-schema.ts (3)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Input validation schemas for server actions should be defined using Zod in dedicated .validation.ts files, which can be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Define validation schemas using Zod
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.
apps/web/components/ui/tooltip.tsx (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use shadcn/ui components when available
Learnt from: aryanprince
PR: elie222/inbox-zero#210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-06-23T12:26:59.468Z
Learning: For components with onClick handlers in Next.js App Router, ensure they are client components by including the 'use client' directive at the top of the file.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Custom React hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: Applies to **/*.tsx : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling; success toast is optional
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Each custom React hook should have a single responsibility and be kept as simple as possible.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: For form text inputs in React using Shadcn UI, use the `Input` component with `registerProps` from a form library (such as react-hook-form) and pass any validation errors to the `error` prop.
apps/web/utils/scheduled-actions/executor.test.ts (3)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-06-23T12:26:27.693Z
Learning: When writing tests for LLM-related functionality in TypeScript (e.g., in 'apps/web/__tests__/*.test.ts'), always create helper functions for common test data to ensure consistency and reduce duplication.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When using Vitest for testing, each test should be independent, use descriptive test names, mock external dependencies, and clean up mocks between tests.
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (19)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: For form text inputs in React using Shadcn UI, use the `Input` component with `registerProps` from a form library (such as react-hook-form) and pass any validation errors to the `error` prop.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Validate form inputs before submission
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Use shadcn/ui components when available
Learnt from: aryanprince
PR: elie222/inbox-zero#210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-06-23T12:27:33.499Z
Learning: For multi-line text input, use the `Input` component with `type="text"`, `autosizeTextarea`, and `rows` props to create an autosizing textarea, and handle validation and errors similarly to single-line inputs.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Show validation errors inline next to form fields
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Input validation schemas for server actions should be defined using Zod in dedicated .validation.ts files, which can be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.{ts,tsx} : Use descriptive error messages
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-06-23T12:27:17.978Z
Learning: Error responses should use generic messages and consistent formats (e.g., SafeError) to avoid leaking sensitive information or internal logic.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-06-23T12:27:05.686Z
Learning: Error messages returned to clients should not reveal internal details or sensitive data; use generic error messages and SafeError for consistent and secure error responses.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Each custom React hook should have a single responsibility and be kept as simple as possible.
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-03T12:02:53.001Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: Custom React hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-03T12:02:16.137Z
Learning: Applies to apps/web/**/*.tsx : Follow consistent naming conventions (PascalCase for components)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-03T12:02:38.024Z
Learning: For mutating data, use Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-06-23T12:26:16.769Z
Learning: For data fetching in custom React hooks, prefer using the useSWR hook. The custom hook should typically wrap useSWR, handle the API endpoint URL, and return the data, loading state, error state, and potentially the mutate function from SWR.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-06-23T12:27:23.938Z
Learning: Server actions must only be used for mutations (create, update, delete operations) and never for data fetching (GET operations); data fetching should be handled via dedicated GET API routes and SWR hooks.
🧬 Code Graph Analysis (9)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
apps/web/hooks/useRule.tsx (1)
  • useRule (4-8)
apps/web/utils/ai/choose-rule/run-rules.ts (2)
apps/web/utils/scheduled-actions/scheduler.ts (2)
  • cancelScheduledActions (175-256)
  • scheduleDelayedActions (121-170)
apps/web/utils/action-item.ts (1)
  • sanitizeActionFields (176-252)
apps/web/utils/scheduled-actions/scheduler.test.ts (2)
apps/web/utils/delayed-actions.ts (1)
  • canActionBeDelayed (15-17)
apps/web/utils/scheduled-actions/scheduler.ts (1)
  • cancelScheduledActions (175-256)
apps/web/utils/ai/assistant/chat.ts (1)
apps/web/utils/actions/rule.validation.ts (1)
  • delayInMinutesSchema (11-15)
apps/web/components/TooltipExplanation.tsx (2)
apps/web/components/ui/tooltip.tsx (3)
  • TooltipProvider (61-61)
  • TooltipTrigger (61-61)
  • TooltipContent (61-61)
apps/web/utils/index.ts (1)
  • cn (4-6)
apps/web/utils/actions/rule.ts (1)
apps/web/utils/date.ts (1)
  • ONE_WEEK_MINUTES (12-12)
apps/web/utils/actions/rule.validation.ts (1)
apps/web/utils/date.ts (1)
  • NINETY_DAYS_MINUTES (13-13)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
apps/web/utils/actions/rule.validation.ts (1)
  • delayInMinutesSchema (11-15)
apps/web/components/ui/tooltip.tsx (1)
apps/web/utils/index.ts (1)
  • cn (4-6)
🔇 Additional comments (60)
apps/web/utils/date.ts (1)

10-14: LGTM! Time constants are mathematically correct and well-named.

The new minute-based constants follow the existing pattern and provide the standardized time units needed for the delayed actions feature. The calculations are accurate and the naming is consistent with the existing millisecond constants.

apps/web/components/ui/tooltip.tsx (2)

8-19: TooltipProvider implementation looks good.

The functional component approach with default delayDuration={0} and data-slot attributes follows modern React patterns. The explicit props handling is clean.


21-29: Tooltip component correctly integrates TooltipProvider.

The automatic wrapping with TooltipProvider ensures consistent delay behavior across all tooltip instances.

apps/web/__tests__/ai-choose-rule.test.ts (1)

79-79: Correct addition of delayInMinutes field.

The delayInMinutes: null addition properly aligns the test data with the updated Action schema for the delayed actions feature.

apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (2)

136-136: Correct addition of delayInMinutes for LABEL action.

The delayInMinutes: null addition properly aligns the cold email blocker rule with the updated action schema.


152-152: Correct addition of delayInMinutes for ARCHIVE action.

The delayInMinutes: null addition is consistent with the schema changes and appropriate for cold email blocker rules.

apps/web/utils/ai/types.ts (1)

25-25: LGTM: Clean type extension for delayed actions support.

The addition of the optional delayInMinutes field properly supports the new delayed actions feature with appropriate typing (number | null).

apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)

31-31: LGTM: Proper destructuring of mutate function from useRule hook.

The addition of mutate to the destructuring enables optimistic UI updates in the RuleForm component, which improves user experience.

apps/web/app/(app)/[emailAccountId]/assistant/DraftReplies.tsx (1)

95-95: LGTM: Proper addition of delayInMinutes field for type consistency.

Adding delayInMinutes: null to the optimistic update object maintains consistency with the extended ActionItem type. Setting it to null is appropriate for draft replies, which shouldn't have delays.

apps/web/utils/rule/rule.ts (1)

350-350: LGTM: Essential mapping for delayInMinutes field persistence.

The addition of delayInMinutes: a.delayInMinutes to the mapActionFields function ensures the delayed action field is properly persisted to the database, completing the data flow for the delayed actions feature.

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

208-208: LGTM: Improved text casing for consistency.

The change to lowercase "skip inbox" aligns with the previous feedback about using sentence case for better readability and modern UI patterns.


210-212: LGTM: Well-integrated delayed archive option.

The addition of the "Label + archive after a week" option effectively integrates the delayed actions feature into the onboarding flow. The option text is clear and user-friendly.

apps/web/utils/ai/rule/create-rule-schema.ts (2)

7-7: LGTM! Good use of reusable validation schema.

The import of delayInMinutesSchema from the dedicated validation module follows the established pattern for schema reuse.


80-80: Proper integration of delay validation.

The delayInMinutes field is correctly added to the action schema using the imported validation schema, ensuring consistent validation across the system.

apps/web/utils/action-item.ts (2)

163-174: Correct type extension for delayed actions.

The ActionFieldsSelection type is properly extended to include delayInMinutes, maintaining consistency with the Prisma schema.


179-189: Proper initialization of delayInMinutes field.

The delayInMinutes field is correctly initialized in the base object and handled consistently with other nullable fields.

apps/web/utils/ai/choose-rule/run-rules.ts (6)

20-24: Good imports for delayed actions functionality.

The imports for scheduling utilities and lodash groupBy are appropriate for the delayed actions feature implementation.


106-110: Effective partitioning of immediate vs delayed actions.

The use of lodash groupBy with a clear predicate effectively separates actions based on whether they have a positive delay. This addresses the past review comment about using lodash groupBy for cleaner code.


123-123: Correct handling of immediate actions in executed rule.

Only immediate actions are saved as ExecutedActions, which is appropriate since delayed actions are handled separately through the scheduling system.


128-143: Proper delayed action scheduling and cancellation.

The logic correctly:

  1. Cancels existing scheduled actions to avoid duplicates when rules re-fire
  2. Schedules new delayed actions only for automated, non-test rules
  3. Handles the case where delayed actions exist

This addresses the past review comments about cancellation placement and rescheduling behavior.


145-146: Correct execution condition for immediate actions.

The condition properly checks for automated rules and immediate actions before executing, ensuring delayed actions are not executed immediately.


219-225: Proper exclusion of delayInMinutes from executed actions.

The destructuring to exclude delayInMinutes from executedActionFields is correct since executed actions represent completed actions, not scheduled ones.

apps/web/utils/ai/assistant/chat.ts (4)

18-18: Correct import of reusable validation schema.

The import of delayInMinutesSchema from the validation module follows the established pattern for schema reuse, addressing the past review comment about duplicated zod types.


95-95: Proper integration of delay validation in action schema.

The delayInMinutes field is correctly added to the action schema using the imported validation schema.


119-119: Correct type extension for delayed actions result.

The UpdateRuleActionsResult type is properly extended to include the optional delayInMinutes field.


759-759: Proper handling of delayInMinutes in rule actions update.

The delayInMinutes value is correctly extracted from the action input and passed to the updateRuleActions function, ensuring delays are preserved when updating rules.

apps/web/utils/actions/rule.validation.ts (4)

9-9: Correct import of date constant.

The import of NINETY_DAYS_MINUTES from date utilities is appropriate for defining the maximum delay constraint.


11-15: Well-defined delay validation schema.

The delayInMinutesSchema is properly constructed with:

  • Appropriate minimum (1 minute) and maximum (90 days) constraints
  • Nullish type allowing optional delays
  • Clear validation messages for constraint violations

This provides a reusable validation schema for delayed actions across the system.


83-83: Proper integration of delay validation in action schema.

The delayInMinutes field is correctly added to the zodAction schema using the dedicated validation schema, ensuring consistent validation across all action definitions.


174-179: Appropriate extension of category actions.

The addition of "label_archive_delayed" to the categoryAction enum properly supports the delayed action feature for onboarding and category-based rules.

apps/web/components/TooltipExplanation.tsx (1)

31-53: Well-implemented tooltip enhancement!

The refactoring to a controlled tooltip system with placement control is a good improvement. The side prop addition addresses the positioning needs mentioned in the past review comments.

apps/web/utils/scheduled-actions/scheduler.test.ts (1)

22-39: Good test coverage for action delay eligibility.

The tests comprehensively cover both supported and unsupported action types, ensuring the canActionBeDelayed function behaves correctly.

apps/web/utils/actions/rule.ts (3)

80-101: Proper implementation of delayInMinutes in action creation.

The delayInMinutes field is correctly integrated into the action creation flow, maintaining consistency with other action fields.


232-232: Consistent handling of delayInMinutes in rule updates.

The field is properly integrated into both update and create operations within the rule update flow.

Also applies to: 251-251


557-567: Well-implemented delayed archive feature in onboarding.

The delayed archive option is properly integrated with:

  • Correct delay value (ONE_WEEK_MINUTES)
  • Appropriate prompt instruction updates
  • Consistent handling across create and update operations

Also applies to: 598-606, 622-627

apps/web/utils/scheduled-actions/executor.test.ts (1)

1-33: Excellent test setup following project conventions.

The mock configuration is comprehensive and properly isolates the executor logic for testing.

apps/web/app/api/scheduled-actions/execute/route.ts (3)

1-18: Proper security setup with QStash verification and validation.

The route correctly implements:

  • QStash signature verification for security
  • Zod schema validation for input
  • Appropriate max duration for long-running tasks

28-76: Excellent validation and status checking.

The implementation properly:

  • Validates input with detailed error logging
  • Handles missing scheduled actions
  • Gracefully handles cancelled and non-pending actions
  • Returns appropriate HTTP status codes

77-103: Well-implemented execution flow with concurrency control.

The implementation correctly:

  • Prevents duplicate processing with atomic status updates
  • Handles both success and failure cases appropriately
  • Provides detailed logging for debugging
  • Returns proper HTTP status codes
apps/web/utils/scheduled-actions/executor.ts (3)

23-105: Well-structured execution orchestration!

The function properly handles the scheduled action execution flow with comprehensive error handling, structured logging, and clear status management.


107-155: Robust email validation with proper error handling!

The function correctly handles Gmail API errors and distinguishes between "not found" and other errors. The conversion to EmailForAction format is complete and type-safe.


240-306: Clean helper functions with proper state management!

The helper functions correctly handle database updates and status transitions with appropriate logging.

.cursor/rules/features/delayed-actions.mdc (1)

1-219: Comprehensive feature documentation!

The documentation thoroughly covers the delayed actions feature with clear explanations of architecture, implementation details, and usage examples. The QStash integration benefits and migration safety considerations are particularly well-documented.

apps/web/utils/scheduled-actions/scheduler.ts (4)

118-170: Clean implementation of batch scheduling!

The function efficiently filters and schedules multiple delayed actions. The non-null assertion on line 148 is safe due to the filter conditions.


172-256: Robust cancellation logic with proper error handling!

The function correctly cancels QStash messages first and handles partial failures gracefully without failing the entire operation.


258-336: Proper handling of QStash unavailability!

The function correctly fails fast when QStash is unavailable and updates the database status to reflect the failure, preventing orphaned scheduled actions.


357-382: Excellent race condition prevention!

The atomic update with status condition effectively prevents duplicate execution of scheduled actions.

apps/web/prisma/schema.prisma (3)

330-346: Correct addition of delay field to Action model!

The optional delayInMinutes field is properly typed and maintains backward compatibility.


429-461: Well-designed ScheduledAction model!

The model properly addresses previous concerns:

  • No restrictive unique constraints that would prevent multiple actions of the same type
  • Clear field naming with scheduledId instead of ambiguous names
  • Appropriate indexes for query performance
  • Proper cascade behavior on relations

892-904: Comprehensive status tracking enums!

Both enums provide necessary states for tracking the lifecycle of scheduled actions and their scheduling status.

apps/web/prisma/migrations/20250626043046_add_scheduled_actions/migration.sql (1)

1-55: Safe and comprehensive migration!

The migration properly uses IF NOT EXISTS clauses for idempotency and creates all necessary database objects with appropriate constraints and indexes.

apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (9)

173-173: Use consistent naming for mutate functions

The rename from mutate to mutateLabels improves clarity by making the purpose explicit. Good change!


209-242: Well-implemented optimistic updates

The optimistic update pattern for delayInMinutes is properly implemented with appropriate error handling and revalidation. This prevents UI inconsistencies when the modal is reopened immediately after saving.


335-335: Logical grouping of action types

Moving the Digest action after Mark spam creates a more logical grouping of organizational actions, improving the user experience.


911-911: Consistent tooltip positioning

The tooltip positioning changes to side="left" improve UI consistency and prevent tooltips from being cut off at the screen edge.

Also applies to: 1148-1148, 1436-1436


1184-1194: Appropriate input type for AI-generated labels

Using a text input for AI-generated labels is the correct approach, as it allows users to enter template syntax like {{prompt}} which wouldn't work with a combobox.


1241-1247: Improved form registration

Adding name and registerProps to the Input component ensures proper integration with react-hook-form, improving form validation and submission handling.


1287-1327: Well-implemented delay action controls

The delay action implementation is comprehensive with:

  • Proper conditional rendering based on action type support
  • Clear user guidance via tooltip
  • Sensible default of 60 minutes
  • Proper error handling and validation

1498-1563: Clean delay input implementation

The DelayInputControls component provides an excellent user experience with:

  • Intuitive unit selection (minutes/hours/days)
  • Proper input sanitization
  • Clean separation between display and storage formats

1038-1042: Efficient memoization of action delay check

Good use of useMemo to optimize the canActionBeDelayed check, with the correct dependency on action.type.

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: 0

🧹 Nitpick comments (2)
apps/web/utils/scheduled-actions/scheduler.ts (2)

148-148: Non-null assertion is safe but could be more explicit

The non-null assertion on delayInMinutes is safe because the filter on lines 134-138 ensures it's not null and > 0. However, consider removing the assertion and using the value directly since TypeScript should infer it's not null after the filter.

-    const scheduledFor = addMinutes(new Date(), actionItem.delayInMinutes!);
+    const scheduledFor = addMinutes(new Date(), actionItem.delayInMinutes);

53-116: Consider transaction safety for consistency

While the current error handling with status updates provides reasonable consistency, consider wrapping the database operations in a transaction for stronger consistency guarantees, especially in createScheduledAction where a database record is created before QStash scheduling.

// Example with transaction
const scheduledAction = await prisma.$transaction(async (tx) => {
  const action = await tx.scheduledAction.create({
    data: {
      // ... existing data
    },
  });

  const scheduledId = await scheduleMessage({
    payload: { scheduledActionId: action.id },
    delayInMinutes: actionItem.delayInMinutes,
    deduplicationId: `scheduled-action-${action.id}`,
  });

  if (scheduledId) {
    await tx.scheduledAction.update({
      where: { id: action.id },
      data: {
        scheduledId,
        schedulingStatus: "SCHEDULED" as const,
      },
    });
  }

  return action;
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between facf647 and 8cbee38.

📒 Files selected for processing (4)
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (3 hunks)
  • apps/web/components/TooltipExplanation.tsx (2 hunks)
  • apps/web/utils/schedule.test.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/schedule.test.ts
  • apps/web/components/TooltipExplanation.tsx
🧰 Additional context used
📓 Path-based instructions (2)
apps/web/**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • apps/web/CLAUDE.md
**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/form-handling.mdc
🧬 Code Graph Analysis (1)
apps/web/utils/scheduled-actions/scheduler.ts (4)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/ai/types.ts (1)
  • ActionItem (15-26)
apps/web/utils/delayed-actions.ts (1)
  • canActionBeDelayed (15-17)
apps/web/utils/cron.ts (1)
  • getCronSecretHeader (26-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (6)
apps/web/utils/scheduled-actions/scheduler.ts (6)

1-21: LGTM - Clean imports and setup

The imports are well-organized and the QStash client setup properly handles the optional token scenario with null checking.


46-51: Good fix - Validation moved before database creation

The validation for delayInMinutes is now properly placed before creating the database record, which prevents orphaned records if validation fails. This addresses the previous review comment.


175-256: Well-implemented cancellation logic

The function properly handles both QStash cancellation and database updates with appropriate error handling. The resilient approach of continuing with database updates even if QStash cancellation fails is good design.


301-318: Good fix - Prevents orphaned actions when QStash is unavailable

The function now properly handles the case when QStash client is not available by updating the database record with "FAILED" status and throwing an error. This prevents orphaned actions and addresses the previous review comment.


341-355: LGTM - Clean QStash message cancellation

The function properly uses the QStash HTTP API for message cancellation with appropriate error handling and logging.


361-382: Excellent concurrency control implementation

The function uses the database status as a condition in the where clause to prevent duplicate execution, which is a robust approach for handling concurrent access. The graceful failure pattern is appropriate for this use case.

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: 0

🧹 Nitpick comments (3)
apps/web/utils/scheduled-actions/scheduler.ts (3)

141-141: Remove non-null assertion - it's safe after filter validation.

The non-null assertion on actionItem.delayInMinutes! is unnecessary since the filter on lines 127-132 already ensures delayInMinutes is not null and is greater than 0.

-    const scheduledFor = addMinutes(new Date(), actionItem.delayInMinutes!);
+    const scheduledFor = addMinutes(new Date(), actionItem.delayInMinutes);

286-304: Inconsistent error handling when QStash client is unavailable.

The code now correctly throws an error when QStash client is unavailable and updates the scheduling status to "FAILED", which is an improvement over the previous implementation that would create orphaned records. However, this creates inconsistency with the error handling in the catch block.

Consider whether the scheduling status update should be done in the catch block as well, or if the error should be thrown immediately without the status update here.


305-320: Ensure consistent scheduling status updates on all failures.

The catch block updates the scheduling status to "FAILED" when scheduling fails, which is good. However, there's a potential issue if the database update itself fails - the error would be swallowed by the subsequent throw error.

Consider wrapping the database update in a try-catch to handle potential database failures gracefully:

  } catch (error) {
    logger.error("Failed to schedule with QStash", {
      error,
      scheduledActionId: payload.scheduledActionId,
      deduplicationId,
    });

-    await prisma.scheduledAction.update({
-      where: { id: payload.scheduledActionId },
-      data: {
-        schedulingStatus: "FAILED" as const,
-      },
-    });
+    try {
+      await prisma.scheduledAction.update({
+        where: { id: payload.scheduledActionId },
+        data: {
+          schedulingStatus: "FAILED" as const,
+        },
+      });
+    } catch (dbError) {
+      logger.error("Failed to update scheduling status", {
+        scheduledActionId: payload.scheduledActionId,
+        error: dbError,
+      });
+    }

    throw error;
  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cbee38 and e308f44.

📒 Files selected for processing (1)
  • apps/web/utils/scheduled-actions/scheduler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
apps/web/**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • apps/web/CLAUDE.md
**/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/form-handling.mdc
⏰ 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: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (6)
apps/web/utils/scheduled-actions/scheduler.ts (6)

1-11: LGTM! Clean imports and logger setup.

The imports are well-organized with proper TypeScript types and the scoped logger configuration follows best practices.


13-20: LGTM! Well-defined interface and QStash client factory.

The ScheduledActionPayload interface is properly typed and the getQstashClient function correctly handles the optional token configuration.


43-47: LGTM! Validation has been moved to the correct location.

The validation for delayInMinutes is now properly placed before the database record creation, addressing the previous review comment about preventing orphaned records.


339-360: LGTM! Proper atomic update with appropriate error handling.

The markQStashActionAsExecuting function correctly uses atomic updates with a WHERE clause condition to prevent race conditions and handles failures gracefully by returning null.


323-337: LGTM! Clean QStash message cancellation implementation.

The cancelMessage function properly handles the QStash HTTP API for message cancellation with appropriate error handling and logging.


165-245: LGTM! Comprehensive cancellation logic with proper error handling.

The cancelScheduledActions function properly handles both QStash message cancellation and database status updates, with appropriate error handling that doesn't fail the entire operation if QStash cancellation fails.

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: 0

🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

128-128: Improve type safety by avoiding any type

The mutate function parameter uses any types which goes against the coding guidelines. Consider using proper SWR mutate function typing.

-  mutate?: (data?: any, options?: any) => void;
+  mutate?: () => void;

Or use the proper SWR mutate function type if you need the full signature.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffd6d6e and e3fef98.

📒 Files selected for processing (2)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (19 hunks)
  • apps/web/utils/ai/assistant/chat.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/ai/assistant/chat.ts
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/**/*.tsx

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

If you need to use onClick in a component, that component is a client component and file must start with 'use client'

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on 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 assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
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 a title element 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.
Assign tabIndex to non-interactive HTML elements with aria-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 a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
🧠 Learnings (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (62)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.118Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/hooks/**/*.{ts,tsx} : Call mutate() after successful mutations to refresh data

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{jsx,tsx} : Use semantic elements instead of role attributes in JSX.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.043Z
Learning: Applies to components/**/*.tsx : Use the Input component for text inputs, passing registerProps and error props for form handling

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.043Z
Learning: Applies to components/**/*.tsx : Use the Input component with autosizeTextarea and appropriate props for text areas

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.098Z
Learning: Applies to **/*.tsx : Validate form inputs before submission

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{html,jsx,tsx} : Make sure label elements have text content and are associated with an input.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{jsx,tsx} : Don't use event handlers on non-interactive elements.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{jsx,tsx} : Don't define React components inside other components.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement early returns for invalid inputs in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/**/*.tsx : Use shadcn/ui components when available

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{jsx,tsx} : Don't assign to React component props.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.098Z
Learning: Applies to **/*.ts : The same validation should be done in the server action too

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.098Z
Learning: Applies to **/*.tsx : Show validation errors inline next to form fields

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.220Z
Learning: Applies to apps/web/utils/actions/*.ts : If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.098Z
Learning: Use descriptive error messages

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.618Z
Learning: Form handling using React Hook Form and Zod

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.618Z
Learning: Util functions

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.480Z
Learning: Applies to apps/web/utils/**/*.ts : Import specific lodash functions to minimize bundle size

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Remove redundant terms from logical expressions.

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/components/**/*.tsx : Use React Hook Form with Zod validation for form handling

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Remove excessive whitespace and truncate long inputs in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{html,jsx,tsx} : Use valid values for the autocomplete attribute on input elements.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.098Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{ts,tsx} : Don't use any or unknown as type constraints.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{ts,tsx} : Don't use implicit any type on variable declarations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{ts,tsx} : Don't use user-defined types.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{ts,tsx} : Don't use empty type parameters in type aliases and interfaces.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.696Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.696Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : Keep custom hooks focused on a single responsibility.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use unnecessary labels.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.287Z
Learning: Applies to **/*.{html,jsx,tsx} : Don't use explicit role property that's the same as the implicit/default role.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.118Z
Learning: Applies to apps/web/components/**/*Form.tsx : Use React Hook Form with Zod resolver for form handling and validation.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.618Z
Learning: Guidelines for creating custom React hooks

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-07-18T15:07:12.388Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Always define types for variant flags

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.118Z
Learning: Applies to apps/web/hooks/**/*.ts : Call mutate() after successful mutations to refresh SWR data on the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.118Z
Learning: Applies to apps/web/components/**/*.tsx : Use the LoadingContent component to handle loading and error states consistently in data-fetching components.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.809Z
Learning: Applies to app/**/*.{ts,tsx} : For mutating data, use Next.js server actions

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component to handle loading and error states consistently

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.696Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : Create dedicated hooks for specific data types (e.g., useAccounts, useLabels).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.696Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : Name custom hooks with the use prefix (e.g., useAccounts.ts).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.118Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use revalidatePath in server actions to invalidate cache after mutations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.220Z
Learning: Applies to apps/web/utils/actions/*.ts : 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). For data fetching, use dedicated GET API Routes combined with SWR Hooks.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/app/api/**/route.ts : Do NOT use POST API routes for mutations - use server actions instead

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.448Z
Learning: Applies to apps/web/**/*.tsx : Use result?.serverError with toastError and toastSuccess

Learnt from: edulelis
PR: #576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses [key: string]: DigestItem[] | undefined | string | Date | undefined instead of intersection types like & Record<string, DigestItem[] | undefined> due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-07-18T15:07:12.388Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use kebab-case for feature flag keys (e.g., 'inbox-cleaner', 'pricing-options-2')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.220Z
Learning: Applies to apps/web/utils/actions/*.ts : Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.220Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

🧬 Code Graph Analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (8)
apps/web/hooks/useRule.tsx (1)
  • useRule (4-8)
apps/web/hooks/useLabels.ts (1)
  • useLabels (37-52)
apps/web/components/TooltipExplanation.tsx (1)
  • TooltipExplanation (33-60)
apps/web/hooks/useFeatureFlags.ts (1)
  • useDelayedActionsEnabled (45-47)
apps/web/utils/delayed-actions.ts (1)
  • canActionBeDelayed (15-17)
apps/web/components/Input.tsx (2)
  • Input (34-112)
  • Label (116-132)
apps/web/components/Toggle.tsx (1)
  • Toggle (19-70)
apps/web/components/ui/select.tsx (4)
  • SelectTrigger (116-116)
  • SelectValue (115-115)
  • SelectContent (117-117)
  • SelectItem (119-119)
⏰ 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: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (7)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (7)

92-93: LGTM: Clean imports for delayed actions feature

The new imports properly support the delayed actions functionality and follow the established path alias patterns.


102-115: LGTM: Proper SWR mutate function integration

Good addition of the mutate function from useRule hook to enable optimistic updates in the RuleForm component.


208-241: LGTM: Well-implemented optimistic updates

The optimistic update logic is properly implemented following SWR patterns:

  • Updates UI immediately with optimistic data
  • Reverts on error by calling mutate()
  • Revalidates after success to ensure data consistency

The implementation correctly handles the delayInMinutes field updates.


991-1291: LGTM: Comprehensive delayed actions UI implementation

Excellent implementation of the delayed actions UI in ActionCard:

  • Proper feature flag integration with useDelayedActionsEnabled
  • Performance-optimized actionCanBeDelayed computation using useMemo
  • Clean toggle interface for enabling/disabling delays
  • Well-integrated DelayInputControls component
  • Proper error handling and validation feedback

The UI layout and conditional rendering logic are well-structured.


1111-1114: LGTM: Improved tooltip positioning

Good improvement to tooltip positioning using the side="left" prop for better user experience.


1462-1527: LGTM: Well-designed DelayInputControls component

Excellent implementation of the delay input controls:

  • Clean functional component structure
  • Proper TypeScript typing
  • Good use of shadcn/ui components (Input, Select)
  • Proper React Hook Form integration with setValue
  • Clear separation of concerns with dedicated handlers

The component follows the coding guidelines for functional components and shadcn/ui usage.


1529-1559: LGTM: Solid utility functions for time conversion

Well-implemented helper functions:

  • getDisplayValueAndUnit: Properly converts minutes to user-friendly display format
  • convertToMinutes: Correctly handles conversion from display format back to minutes
  • Good edge case handling for null/undefined values
  • Clear logic for different time units (minutes, hours, days)
  • Functions are pure and have single responsibilities

The conversion logic is mathematically correct and handles the common time unit scenarios appropriately.

@elie222 elie222 merged commit 249a902 into elie222:main Jul 21, 2025
9 of 11 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jul 21, 2025
@edulelis edulelis deleted the delayed-actions branch July 24, 2025 18:15
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.

2 participants