Skip to content

Multi condition supports. Allow smart categories in rules#270

Merged
elie222 merged 56 commits intomainfrom
loading-onboarding-modal
Dec 21, 2024
Merged

Multi condition supports. Allow smart categories in rules#270
elie222 merged 56 commits intomainfrom
loading-onboarding-modal

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Dec 17, 2024

Show an onboarding modal while waiting to set rules

Summary by CodeRabbit

  • New Features

    • Introduced a modal dialog for processing prompt files with a guided carousel.
    • Added a new carousel component for enhanced user interaction.
    • Updated the prompt generation button for improved visual representation.
    • Enhanced the user interface for reporting mistakes related to rule mismatches.
    • Implemented a new function to determine if a message is a reply in a thread.
    • Added support for managing dynamic conditions in rule forms.
    • Introduced new condition schemas for improved rule validation.
    • Added a new logical operator for categorizing rules.
    • Enhanced the structure of rules by including conditions in the display.
    • Expanded data retrieval for user rules to include category filters.
    • Added a new function for extracting email data from messages.
    • Updated the description for the rule name field to clarify naming conventions.
    • Enhanced progress feedback with a loading spinner in the progress panel.
    • Added new utility functions for matching rules based on conditions.
    • Introduced functionality for testing cold emails with user interaction.
  • Bug Fixes

    • Enhanced error handling in email processing logic and rule testing.
  • Chores

    • Removed obsolete API endpoint for checking Gmail threads.
    • Removed unused functions related to Gmail thread checking.
    • Deleted a utility function for converting rule types to strings.
    • Updated test files and dependencies to improve testing framework compatibility.
    • Modified environment variable requirements to make certain keys optional.

@vercel
Copy link

vercel bot commented Dec 17, 2024

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

Name Status Preview Updated (UTC)
inbox-zero ✅ Ready (Inspect) Visit Preview Dec 21, 2024 5:44pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2024

Warning

Rate limit exceeded

@elie222 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b921c2b and 25490c4.

📒 Files selected for processing (4)
  • apps/web/app/(app)/automation/RuleForm.tsx (16 hunks)
  • apps/web/utils/__mocks__/prisma.ts (1 hunks)
  • apps/web/utils/condition.ts (1 hunks)
  • apps/web/utils/llms/config.ts (1 hunks)

Walkthrough

This pull request introduces a comprehensive update to the web application's automation and email processing components. The changes focus on enhancing user interface interactions, particularly in rule processing, thread handling, and introducing a new carousel-based dialog for prompt file processing. Key modifications include adding a new ProcessingPromptFileDialog component, removing thread-checking API endpoints, and streamlining the logic for handling email threads and rule applications.

Changes

File Change Summary
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx New React component for managing prompt file processing dialog with multi-step carousel
apps/web/app/(app)/automation/ReportMistake.tsx Refactored thread message handling, removed useSWR for thread checks
apps/web/app/(app)/automation/RulesPrompt.tsx Added dialog state, result tracking, and visual improvements to prompt generation
apps/web/app/(app)/automation/TestRules.tsx Updated button icons and simplified testing UI
apps/web/app/api/google/threads/[id]/check/route.ts Removed thread checking API endpoint
apps/web/app/api/google/webhook/process-history.ts Updated email processing logic, introduced shouldRunColdEmailBlocker
apps/web/components/SearchForm.tsx Removed search button from form
apps/web/components/ui/carousel.tsx New carousel component using embla-carousel-react
apps/web/utils/actions/ai-rule.ts Simplified thread and message processing logic
apps/web/utils/gmail/thread.ts Removed hasMultipleMessages function
apps/web/utils/thread.ts Added isReplyInThread utility function
apps/web/app/(app)/automation/RuleForm.tsx Enhanced dynamic condition handling and error display
apps/web/app/(app)/automation/group/CreateGroupModal.tsx Simplified button text for group creation
apps/web/app/(app)/automation/group/ViewGroup.tsx Updated registration method for form fields
apps/web/app/(app)/automation/rule/[ruleId]/page.tsx Added conditions property to rule data structure
apps/web/app/(app)/automation/rule/create/page.tsx Modified rule object construction to include conditions
apps/web/app/(app)/bulk-archive/page.tsx Simplified registration process for form fields
apps/web/app/(app)/settings/EmailUpdatesSection.tsx Updated form field registration method
apps/web/app/(app)/settings/ModelSection.tsx Simplified registration for AI model selection
apps/web/app/(landing)/welcome/form.tsx Simplified routing logic based on variant state
apps/web/app/(landing)/welcome/survey.ts Updated survey question choices
apps/web/components/Select.tsx Refactored Select component for better type safety and ref handling
apps/web/prisma/migrations/20241218123405_multi_conditions/migration.sql Added new LogicalOperator enum and modified Rule table structure
apps/web/prisma/schema.prisma Updated Rule model and added unique constraints
apps/web/utils/actions/rule.ts Streamlined rule creation and update logic using flattened conditions
apps/web/utils/actions/validation.ts Introduced new condition schemas and updated rule body validation
apps/web/utils/condition.ts New utilities for managing rule conditions and flattening logic
apps/web/utils/llms/config.ts Enhanced configuration handling for OLLAMA provider

Sequence Diagram

sequenceDiagram
    participant User
    participant ProcessingPromptFileDialog
    participant RulesPromptForm
    participant Backend

    User->>RulesPromptForm: Click "Give me ideas"
    RulesPromptForm->>Backend: Generate rules prompt
    Backend-->>RulesPromptForm: Return generated prompt
    RulesPromptForm->>ProcessingPromptFileDialog: Open dialog
    ProcessingPromptFileDialog->>ProcessingPromptFileDialog: Show loading state
    ProcessingPromptFileDialog->>ProcessingPromptFileDialog: Display carousel steps
    ProcessingPromptFileDialog-->>User: Show processing results
Loading

Possibly related PRs

  • Generate prompt for user based on their history/labels #232: The changes in this PR introduce a new button in the RulesPromptForm component for generating rules prompts, which is directly related to the functionality of the ProcessingPromptFileDialog component in the main PR that manages user interactions for processing prompts.
  • Generate groups with AI prompt #244: This PR focuses on generating groups based on AI prompts, which aligns with the overall theme of enhancing user interactions and automating processes, similar to the updates made in the ProcessingPromptFileDialog component of the main PR.

Poem

🐰 Hopping through code with glee,
Carousels spin, rules set free!
Dialogs dance, threads unwind,
AI's magic, cleverly designed.
Sparkling changes, rabbit's delight! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🔭 Outside diff range comments (1)
apps/web/app/(app)/automation/RulesPrompt.tsx (1)

Line range hint 123-147: Include all necessary dependencies in useCallback

To ensure that the onSubmit function remains consistent and up-to-date, include all state setters used within the function in the dependency array of the useCallback hook. This helps prevent stale closures and potential bugs.

Suggested change:

-     [mutate],
+     [mutate, setIsSubmitting, setIsDialogOpen, setResult],
🧹 Nitpick comments (7)
apps/web/components/ui/carousel.tsx (2)

210-215: Adjust button positioning for better UI consistency

The positioning of the previous and next buttons uses negative absolute positioning (e.g., -left-12, -right-12, -top-12, -bottom-12). This can cause issues on different screen sizes or if the carousel container size changes.

Consider using relative positioning or CSS variables to make the button positions more adaptable:

  className={cn(
    "absolute h-8 w-8 rounded-full",
-   orientation === "horizontal"
-     ? "-left-12 top-1/2 -translate-y-1/2"
-     : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
+   orientation === "horizontal"
+     ? "left-2 top-1/2 -translate-y-1/2"
+     : "top-2 left-1/2 -translate-x-1/2 rotate-90",
    className,
  )}

154-173: Add displayName to improve debugging

While you've set CarouselContent.displayName = "CarouselContent";, it's good practice to set displayName for all components, especially when using React.forwardRef, to aid in debugging and developer tools.

Ensure all components have a displayName:

 const CarouselContent = React.forwardRef<
   HTMLDivElement,
   React.HTMLAttributes<HTMLDivElement>
 >(({ className, ...props }, ref) => {
   const { carouselRef, orientation } = useCarousel();

   return (
     <div ref={carouselRef} className="overflow-hidden">
       <div
         ref={ref}
         className={cn(
           "flex",
           orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
           className,
         )}
         {...props}
       />
     </div>
   );
 });
 CarouselContent.displayName = "CarouselContent";
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (2)

149-154: Enhance image responsiveness with the 'sizes' attribute

To optimize image loading and responsiveness, consider adding the sizes attribute to your <Image> components, especially when using className="w-full". This helps Next.js serve correctly sized images for different viewport sizes.


254-254: Remove unused commented code

Consider removing the commented-out code to keep the codebase clean and maintainable.

apps/web/app/(app)/automation/ReportMistake.tsx (1)

Line range hint 178-209: Improved performance and UX with synchronous thread checking.

The refactoring from async API call to synchronous ID comparison:

  1. Eliminates unnecessary network request
  2. Removes loading state, providing instant feedback
  3. Simplifies the code by removing error handling for API calls

Consider adding a comment explaining the thread detection logic:

+  // Check if message is a reply by comparing message ID with thread ID
   const isThread = isReplyInThread(props.message.id, threadId);
apps/web/app/(app)/automation/TestRules.tsx (2)

131-143: Use theme-aware border color class

The border color is hardcoded to border-gray-200. Consider using the theme-aware class border-border to ensure proper dark mode support and consistent theming.

-<div className="flex items-center justify-between gap-2 border-b border-gray-200 px-6 pb-4">
+<div className="flex items-center justify-between gap-2 border-b border-border px-6 pb-4">

280-281: Consider maintaining consistent button width during loading

The button width changes when transitioning to the loading state due to the icon being hidden. This can cause layout shifts.

-<Button variant="default" loading={isTesting} onClick={onTest}>
-  {!isTesting && <SparklesIcon className="mr-2 h-4 w-4" />}
-  Test
+<Button variant="default" loading={isTesting} onClick={onTest} className="min-w-[5rem]">
+  {!isTesting && <SparklesIcon className="mr-2 h-4 w-4" />}
+  Test
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0f052ac and cac3562.

📒 Files selected for processing (11)
  • apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (1 hunks)
  • apps/web/app/(app)/automation/ReportMistake.tsx (3 hunks)
  • apps/web/app/(app)/automation/RulesPrompt.tsx (7 hunks)
  • apps/web/app/(app)/automation/TestRules.tsx (2 hunks)
  • apps/web/app/api/google/threads/[id]/check/route.ts (0 hunks)
  • apps/web/app/api/google/webhook/process-history.ts (2 hunks)
  • apps/web/components/SearchForm.tsx (2 hunks)
  • apps/web/components/ui/carousel.tsx (1 hunks)
  • apps/web/utils/actions/ai-rule.ts (3 hunks)
  • apps/web/utils/gmail/thread.ts (0 hunks)
  • apps/web/utils/thread.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/utils/gmail/thread.ts
  • apps/web/app/api/google/threads/[id]/check/route.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/web/components/SearchForm.tsx
🔇 Additional comments (9)
apps/web/components/ui/carousel.tsx (1)

161-172: Ensure proper use of orientation in class names

In CarouselContent, the conditional class names for orientation might not correctly apply flex direction for vertical orientation.

Please verify that the class names adjust the layout as intended when orientation is set to "vertical".

✅ Verification successful

Let me gather more information about the orientation handling in the carousel component.


Let me check the Carousel component implementation to see how orientation is provided through the context.


Let me try a different pattern to search for the Carousel implementation.


Let me check one more thing to see how the orientation is passed through the context.


Class names correctly handle vertical orientation

The implementation correctly handles both horizontal and vertical orientations:

  • When orientation="horizontal": applies flex with -ml-4 margin
  • When orientation="vertical": applies flex flex-col with -mt-4 margin

The conditional class names are properly implemented to adjust the layout based on the orientation prop, which is passed through the CarouselContext from the parent Carousel component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that CarouselContent applies correct flex direction based on orientation.

# Test: Search for usage of CarouselContent and check if the orientation prop is passed and handled correctly.
rg -A 5 -B 5 'CarouselContent' apps/web/

# Expected: When orientation is "vertical", the class "flex-col" should be applied.

Length of output: 4654


Script:

#!/bin/bash
# Search for the useCarousel hook implementation to understand how orientation is handled
ast-grep --pattern 'function useCarousel() {
  $$$
}'

# Also check for any context provider that might set the orientation
ast-grep --pattern 'const CarouselContext = $$$'

Length of output: 2517


Script:

#!/bin/bash
# Search for the Carousel component implementation to see how orientation is set
ast-grep --pattern 'const Carousel = React.forwardRef<$_>(({ $$$orientation$$$, $$ }) => {
  $$$
})'

# Also check for any CarouselContextProps type definition
ast-grep --pattern 'type CarouselContextProps = {
  $$$
}'

Length of output: 163


Script:

#!/bin/bash
# Search for the Carousel component and its props
rg -A 10 "const Carousel = " apps/web/components/ui/carousel.tsx

# Search for CarouselContextProps type definition
rg -A 5 "type CarouselContextProps" apps/web/components/ui/carousel.tsx

Length of output: 563


Script:

#!/bin/bash
# Get more context around the CarouselContextProps type and the Carousel component implementation
rg -A 20 "type CarouselContextProps" apps/web/components/ui/carousel.tsx

# Also check the full implementation of CarouselContent
rg -A 15 "const CarouselContent = React\.forwardRef" apps/web/components/ui/carousel.tsx

Length of output: 1145

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

1-3: Function isReplyInThread correctly identifies reply messages

The isReplyInThread function appropriately checks if a message is a reply in the thread by comparing the message ID with the thread ID.

apps/web/app/api/google/webhook/process-history.ts (2)

351-351: Consistent thread detection implementation across files.

The isReplyInThread function is used consistently across multiple locations. This standardization improves maintainability.

Also applies to: 96-96, 140-140


17-17: Verify the thread detection logic change.

The switch to isReplyInThread reduces API calls by comparing IDs instead of fetching thread data. While this improves performance, we should verify that this simpler approach doesn't miss edge cases handled by the previous implementation.

✅ Verification successful

Based on the search results, I can now generate the final response:

Thread detection logic change is safe and well-implemented

The new isReplyInThread implementation is a simple, reliable check that compares message ID with thread ID. This approach is correct because:

  • The implementation in apps/web/utils/thread.ts uses a fundamental Gmail threading property: if a message is a reply, its ID will differ from the thread ID
  • The function is consistently used across multiple components (automation, webhooks, AI rules) showing it's a proven approach
  • The previous implementation's edge cases are inherently covered by this ID-based check, as Gmail's threading model guarantees this relationship
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage and implementation of isReplyInThread function
# Test: Search for the implementation and all usages of isReplyInThread
rg -A 5 'isReplyInThread'

# Test: Search for any tests related to thread detection
rg -A 5 'test.*thread|thread.*test'

Length of output: 3873

apps/web/app/(app)/automation/ReportMistake.tsx (1)

43-43: LGTM: Clean import addition.

The import of isReplyInThread is appropriately placed with other imports.

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

20-20: LGTM: Consistent import across the codebase.

The import of isReplyInThread maintains consistency with other files.


96-96: Performance improvement in rule processing.

The change from fetching thread data to using isReplyInThread improves performance by:

  1. Reducing API calls to Gmail
  2. Simplifying thread detection logic
  3. Maintaining consistent behavior across the application

Also applies to: 140-140

✅ Verification successful

Let me verify the usage of isReplyInThread function to ensure we have a complete understanding.


Performance improvement confirmed with proper thread detection implementation

The verification confirms that isReplyInThread is a lightweight utility function that determines thread status by comparing messageId and threadId, replacing the need for API calls. The function is consistently used across the codebase in:

  • AI rule processing
  • Webhook history processing
  • Automation mistake reporting

The change indeed improves performance by eliminating unnecessary Gmail API calls while maintaining consistent thread detection behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the removal of thread fetching logic
# Test: Search for any remaining thread fetching calls that could be optimized
rg -A 5 'getThread|thread.*fetch|fetch.*thread'

Length of output: 16336


Script:

#!/bin/bash
# Search for isReplyInThread function definition and usage
ast-grep --pattern 'function isReplyInThread($_) { $$$ }'

# Also search for any direct usage
rg 'isReplyInThread' -A 3

Length of output: 2411

apps/web/app/(app)/automation/TestRules.tsx (2)

145-151: LGTM! Clean implementation of the custom form toggle

The conditional rendering and button styling are well implemented.


Line range hint 1-1: Verify integration with ProcessingPromptFileDialog

The AI summary mentions integration with ProcessingPromptFileDialog, but there's no direct interaction visible in this file. Let's verify the integration points.

✅ Verification successful

Integration with ProcessingPromptFileDialog is properly implemented

The verification shows that:

  • ProcessingPromptFileDialog imports TestRulesContent from TestRules.tsx
  • TestRulesContent is properly exported from TestRules.tsx
  • ProcessingPromptFileDialog uses TestRulesContent component in its render logic

The integration between these components is correctly implemented and the "use client" directive in TestRules.tsx is appropriate since it's being used in an interactive client-side context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to TestRules components in ProcessingPromptFileDialog
rg -l "TestRules|TestResult" "apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx"

# Search for shared dependencies between the files
ast-grep --pattern 'import { $_ } from "@/components/$_"' "apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx"

Length of output: 277


Script:

#!/bin/bash
# Let's examine the actual content of ProcessingPromptFileDialog.tsx to see how TestRules is used
rg -A 5 "TestRules|TestResult" "apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx"

# Also check for any imports related to TestRules
rg "import.*TestRules" "apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx"

Length of output: 730


Script:

#!/bin/bash
# Let's check the TestRules.tsx file to see how TestRulesContent is exported and used
rg -A 5 "export.*TestRulesContent" "apps/web/app/(app)/automation/TestRules.tsx"

# Also check if there are any other files using TestRulesContent
rg "TestRulesContent" --type ts --type tsx

Length of output: 374

Comment on lines +115 to +121
api.on("reInit", onSelect);
api.on("select", onSelect);

return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Clean up all event listeners to prevent memory leaks

In the useEffect, you add event listeners for both "reInit" and "select" events. However, the cleanup function only removes the "select" event listener. This could lead to memory leaks as the "reInit" listener is not removed.

Update the cleanup function to remove both event listeners:

  api.on("reInit", onSelect);
  api.on("select", onSelect);

  return () => {
+   api.off("reInit", onSelect);
    api.off("select", onSelect);
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
api.on("reInit", onSelect);
api.on("select", onSelect);
return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);
api.on("reInit", onSelect);
api.on("select", onSelect);
return () => {
api.off("reInit", onSelect);
api.off("select", onSelect);
};
}, [api, onSelect]);

Comment on lines +88 to +98
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
}
},
[scrollPrev, scrollNext],
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Keyboard navigation should handle vertical orientation

The handleKeyDown function only handles horizontal arrow keys (ArrowLeft and ArrowRight), even when the carousel orientation is vertical. Users with vertical orientation might expect to use ArrowUp and ArrowDown keys for navigation.

Consider updating the handleKeyDown function to account for the orientation:

  const handleKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
-     if (event.key === "ArrowLeft") {
+     const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
+     const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
+     if (event.key === prevKey) {
        event.preventDefault();
        scrollPrev();
-     } else if (event.key === "ArrowRight") {
+     } else if (event.key === nextKey) {
        event.preventDefault();
        scrollNext();
      }
    },
    [orientation, scrollPrev, scrollNext],
  );

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +228 to +234
useEffect(() => {
if (!api) return;

api.on("select", () => {
onStepChange(api.selectedScrollSnap());
});
}, [api, onStepChange]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add cleanup function to useEffect to avoid potential memory leaks

When adding event listeners in useEffect, it's important to clean them up to prevent potential memory leaks. Consider returning a cleanup function that removes the event listener.

Suggested change:

useEffect(() => {
  if (!api) return;

  const handler = () => {
    onStepChange(api.selectedScrollSnap());
  };
  api.on("select", handler);

+  return () => {
+    api.off("select", handler);
+  };
}, [api, onStepChange]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!api) return;
api.on("select", () => {
onStepChange(api.selectedScrollSnap());
});
}, [api, onStepChange]);
useEffect(() => {
if (!api) return;
const handler = () => {
onStepChange(api.selectedScrollSnap());
};
api.on("select", handler);
return () => {
api.off("select", handler);
};
}, [api, onStepChange]);

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 (6)
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (6)

48-49: Consider using constants for step management

Define step constants to improve maintainability and prevent magic numbers throughout the code.

+const STEPS = {
+  INITIAL: 0,
+  FINAL: 4,
+} as const;
+
 const [modal, setModal] = useQueryState("modal");
-const [currentStep, setCurrentStep] = useState(0);
+const [currentStep, setCurrentStep] = useState(STEPS.INITIAL);

69-70: Enhance dialog accessibility

Consider adding role="dialog" and aria-labelledby attributes to improve accessibility.

-<DialogContent className="max-h-[90vh] overflow-y-auto px-0 sm:max-w-7xl">
+<DialogContent 
+  className="max-h-[90vh] overflow-y-auto px-0 sm:max-w-7xl"
+  role="dialog"
+  aria-labelledby="dialog-title"
+>
-  <DialogHeader className="px-6">
+  <DialogHeader className="px-6" id="dialog-title">

119-127: Define proper TypeScript interface for result props

Extract the result type to improve reusability and type safety.

+interface RuleProcessingResult {
+  createdRules: number;
+  editedRules: number;
+  removedRules: number;
+}
+
 function ResultContent({
   result,
 }: {
-  result?: {
-    createdRules: number;
-    editedRules: number;
-    removedRules: number;
-  };
+  result?: RuleProcessingResult;
 }) {

156-162: Optimize image handling across step components

  1. The image styling is duplicated across components
  2. Alt text could be more descriptive
  3. Image dimensions should match actual image sizes

Extract the common image component:

+const StepImage = ({ src, alt }: { src: string; alt: string }) => (
+  <Image
+    src={src}
+    alt={alt}
+    width={500}
+    height={300}
+    className="w-full object-contain"
+    priority // Add priority for above-the-fold images
+  />
+);

Use more descriptive alt text:

-alt="Analyzing prompt file"
+alt="AI system analyzing and extracting rules from a prompt file"

Also applies to: 171-177, 188-194, 205-211


244-252: Move steps array outside component

The steps array doesn't depend on any component props or state and can be memoized outside.

+const CAROUSEL_STEPS = [
+  <ProcessingPromptFileDialogStep1 key="step-1" />,
+  <ProcessingPromptFileDialogStep2 key="step-2" />,
+  <ProcessingPromptFileDialogStep3 key="step-3" />,
+  <ProcessingPromptFileDialogStep4 key="step-4" />,
+] as const;
+
 function ProcessingPromptFileDialogCarousel({
   currentStep,
   onStepChange,
 }: {
   currentStep: number;
   onStepChange: (step: number) => void;
 }) {
   const [api, setApi] = useState<CarouselApi>();
-  const steps = useMemo(
-    () => [
-      <ProcessingPromptFileDialogStep1 key="step-1" />,
-      <ProcessingPromptFileDialogStep2 key="step-2" />,
-      <ProcessingPromptFileDialogStep3 key="step-3" />,
-      <ProcessingPromptFileDialogStep4 key="step-4" />,
-    ],
-    [],
-  );

262-263: Remove commented code

Remove the commented code as it's not providing any value.

-                  {/* <span className="text-3xl font-semibold">{step}</span> */}
                   <span>{step}</span>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cac3562 and 166c221.

📒 Files selected for processing (1)
  • apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (1 hunks)
🔇 Additional comments (2)
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (2)

236-242: Add cleanup function to useEffect

When adding event listeners in useEffect, it's important to clean them up to prevent memory leaks.


59-65: 🛠️ Refactor suggestion

Add cleanup function to useEffect

The useEffect hook should clean up the modal state when the component unmounts.

 useEffect(() => {
   if (open) {
     setCurrentStep(0);
   } else {
     setModal(null);
   }
+  return () => {
+    setModal(null);
+  };
 }, [open, setModal]);

Likely invalid or redundant comment.

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 (5)
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (5)

131-146: Enhance accessibility for loading state

The loading indicator should have proper ARIA attributes to improve accessibility.

-      <Loading />
+      <Loading aria-label="Processing prompt file" />

148-180: Use more semantic HTML for results summary

Consider using <dl> (description list) for better semantic structure of the results data.

-      <ul className="text-sm text-muted-foreground">
-        {result.createdRules > 0 && (
-          <li>{result.createdRules} rules created</li>
-        )}
-        {result.editedRules > 0 && <li>{result.editedRules} rules edited</li>}
-        {result.removedRules > 0 && (
-          <li>{result.removedRules} rules removed</li>
-        )}
-      </ul>
+      <dl className="text-sm text-muted-foreground">
+        {result.createdRules > 0 && (
+          <>
+            <dt className="inline">Created:</dt>
+            <dd className="inline ml-1">{result.createdRules} rules</dd>
+          </>
+        )}
+        {result.editedRules > 0 && (
+          <>
+            <dt className="inline">Edited:</dt>
+            <dd className="inline ml-1">{result.editedRules} rules</dd>
+          </>
+        )}
+        {result.removedRules > 0 && (
+          <>
+            <dt className="inline">Removed:</dt>
+            <dd className="inline ml-1">{result.removedRules} rules</dd>
+          </>
+        )}
+      </dl>

185-191: Standardize image handling across dialog steps

Image dimensions and styling are inconsistent across different steps. Some use fixed dimensions while others use w-full class. Consider standardizing the approach:

-      <Image
-        src="/images/automation/rules.png"
-        alt="Analyzing prompt file"
-        width={1000}
-        height={500}
-        className="rounded-lg"
-      />
+      <Image
+        src="/images/automation/rules.png"
+        alt="Analyzing prompt file"
+        width={700}
+        height={500}
+        className="w-full rounded-lg"
+      />

Also applies to: 205-211, 240-246, 260-266


302-310: Remove unnecessary onNext props in carousel steps

The steps array passes empty onNext handlers to each step component, which are never used since navigation is handled by the carousel.

-      <ProcessingPromptFileDialogStep1 key="step-1" onNext={() => {}} />,
-      <ProcessingPromptFileDialogStep2 key="step-2" onNext={() => {}} />,
-      <ProcessingPromptFileDialogStep3 key="step-3" onNext={() => {}} />,
-      <ProcessingPromptFileDialogStep4 key="step-4" onNext={() => {}} />,
+      <ProcessingPromptFileDialogStep1 key="step-1" />,
+      <ProcessingPromptFileDialogStep2 key="step-2" />,
+      <ProcessingPromptFileDialogStep3 key="step-3" />,
+      <ProcessingPromptFileDialogStep4 key="step-4" />,

This would require updating the step components to make the onNext prop optional.


182-277: Consider creating a shared step component

The step components share similar structure and patterns. Consider creating a shared StepComponent to reduce code duplication:

interface StepProps {
  image: {
    src: string;
    alt: string;
  };
  content: React.ReactNode;
  onNext?: () => void;
}

function DialogStep({ image, content, onNext }: StepProps) {
  return (
    <div className="p-6">
      <Image
        src={image.src}
        alt={image.alt}
        width={700}
        height={500}
        className="w-full rounded-lg"
      />
      <div className="mt-4">{content}</div>
      {onNext && (
        <Button className="mt-2" onClick={onNext}>
          Next
        </Button>
      )}
    </div>
  );
}

This would simplify the implementation of individual steps and ensure consistent styling.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 166c221 and c9cdf8f.

📒 Files selected for processing (1)
  • apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (1 hunks)
🔇 Additional comments (1)
apps/web/app/(app)/automation/ProcessingPromptFileDialog.tsx (1)

294-300: Add cleanup function to useEffect to avoid potential memory leaks

When adding event listeners in useEffect, it's important to clean them up to prevent potential memory leaks.

Comment on lines +52 to +56
// useEffect(() => {
// if (!isLoading && result && currentStep < 4) {
// setCurrentStep(4);
// }
// }, [isLoading, result, currentStep]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented out code

The file contains several blocks of commented out code that should be removed to improve maintainability and readability. If this code is needed for reference, it should be preserved in version control history instead.

Also applies to: 89-105, 107-125

Comment on lines +237 to +254
function ProcessingPromptFileDialogStep3({ onNext }: { onNext: () => void }) {
return (
<div>
<Image
src="/images/automation/rules.png"
alt="Saving rules"
width={500}
height={300}
className="w-full"
/>
<p>Next, you can click on a rule to edit it even further.</p>
<p>Each rule is made up of two parts: a condition and an action.</p>
<p>Our AI sets these up for you, but you can adjust them as needed.</p>
<Button className="mt-2" onClick={onNext}>
Next
</Button>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate content from Step 3

The content about rule structure (condition and action) is duplicated between Step 2 and Step 3. Consider consolidating this information into a single step.

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

🧹 Nitpick comments (20)
apps/web/app/(landing)/welcome/survey.ts (1)

8-11: LGTM! Consider adding tooltips for clarity.

The updated feature names are more concise and user-friendly. However, since these are presented during onboarding, consider adding tooltips or brief descriptions to help users understand what each feature does, especially for technical terms like "AI Categorizer".

 choices: [
-  "AI Personal Assistant",
-  "Bulk Unsubscriber",
-  "Cold Email Blocker",
-  "AI Categorizer",
+  {
+    label: "AI Personal Assistant",
+    description: "Let AI help manage your emails and draft responses"
+  },
+  {
+    label: "Bulk Unsubscriber",
+    description: "Clean up your inbox by mass unsubscribing from newsletters"
+  },
+  {
+    label: "Cold Email Blocker",
+    description: "Automatically filter out unsolicited emails"
+  },
+  {
+    label: "AI Categorizer",
+    description: "Automatically organize emails into relevant categories"
+  },
apps/web/app/(app)/automation/group/ViewGroup.tsx (1)

344-344: Standardize form field registration methods across components

While the Select component now uses the recommended spread operator pattern for react-hook-form registration, the Input component still uses the registerProps prop. Consider standardizing this approach across all form components for better consistency.

Example for the Input component:

      <Input
        type="text"
        name="value"
        placeholder="e.g. elie@getinboxzero.com"
-       registerProps={register("value", { required: true })}
+       {...register("value", { required: true })}
        error={errors.value}
        className="min-w-[250px]"
      />
apps/web/app/(landing)/welcome/form.tsx (2)

Line range hint 193-197: Consider removing unused SkipOnboardingButton implementation

The SkipOnboardingButton component and its usage are commented out. If this feature is no longer needed, consider removing the unused code to improve maintainability.

-  {/* {!isFinalQuestion && (
-    <SkipOnboardingButton
-      searchParams={searchParams}
-      submitPosthog={submitPosthog}
-      posthog={posthog}
-      router={router}
-    />
-  )} */}

Also remove the entire SkipOnboardingButton component definition if it's no longer needed.


Line range hint 241-241: Fix typo in parameter name

The parameter name has a typo: 'seachParams' should be 'searchParams'.

-function getResponses(seachParams: URLSearchParams): Record<string, string> {
+function getResponses(searchParams: URLSearchParams): Record<string, string> {
apps/web/utils/llms/config.ts (2)

Line range hint 3-13: Consider adding type safety for environment variables

While the conditional logic is sound, consider adding runtime checks or type guards to ensure environment variables are properly typed and validated when accessed.

-const supportsOllama = env.OLLAMA_MODEL && env.OLLAMA_BASE_URL;
+const supportsOllama = Boolean(env.OLLAMA_MODEL && env.OLLAMA_BASE_URL);

+type Provider = typeof Provider[keyof typeof Provider];
 export const Provider = {
   OPEN_AI: "openai" as const,
   ANTHROPIC: "anthropic" as const,
   ...(supportsOllama ? { OLLAMA: "ollama" as const } : {}),
 };

Line range hint 24-39: Consider improving maintainability and documentation

While the implementation is correct, there are a few improvements that could make the code more maintainable:

  1. The comment "used in ui only" could be more explicit about why and where this value is used
  2. Consider extracting the model configurations into separate constants for better maintainability
+const ANTHROPIC_MODELS = [
+  {
+    label: "Claude 3.5 Sonnet",
+    value: "claude-3-5-sonnet",
+    // This value is used only for UI display purposes.
+    // The actual model selection between Anthropic and Bedrock is handled at runtime
+  },
+] as const;

 export const modelOptions: Record<string, { label: string; value: string }[]> = {
   [Provider.OPEN_AI]: [
     { label: "GPT-4o", value: Model.GPT_4O },
     { label: "GPT-4o Mini", value: Model.GPT_4O_MINI },
   ],
-  [Provider.ANTHROPIC]: [
-    {
-      label: "Claude 3.5 Sonnet",
-      value: "claude-3-5-sonnet", // used in ui only. can be either anthropic or bedrock
-    },
-  ],
+  [Provider.ANTHROPIC]: ANTHROPIC_MODELS,
   ...(Provider.OLLAMA && Model.OLLAMA
     ? {
         [Provider.OLLAMA]: [{ label: "Ollama", value: Model.OLLAMA }],
       }
     : {}),
 };
apps/web/utils/actions/validation.ts (2)

101-109: Refine uniqueness across condition types
The refinement prevents duplicate condition types in the same rule. This is logical given the current design. If there's a future need to allow multiples of the same type, this logic may need revisiting.


113-123: Commented-out fields
These commented lines might be confusing for new contributors. If deprecated, consider removing them or adding a comment explaining why they remain.

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

180-198: Introducing conditions fields in Rule
• The additional columns (e.g., instructions, groupId, categoryFilterType) and the unique constraint on groupId can be limiting if multiple rules in different contexts need the same group. Ensure that’s desired.

apps/web/app/(app)/automation/RuleForm.tsx (6)

69-69: Trigger usage
You’re calling trigger() presumably to ensure real-time validation for conditions. Looks good, but be mindful of performance if the form grows more complex.


Line range hint 98-109: Label auto-creation
When a label doesn’t exist, you create it on the fly via createLabelAction. Ensure concurrency issues and rate-limiting are handled if multiple rules are saved concurrently.


165-165: Console.log(“errors”, errors)
Logging errors is helpful in development but can clutter production logs. Consider removing or gating with a debug flag.


196-383: Rendering condition fields
• Clear organization for AI, static, group, and category conditions.
• Potentially large form sections can be a bit unwieldy; consider splitting out subcomponents for maintainability.


578-584: Commented-out code
Consider removing or clarifying these commented lines to avoid confusion among collaborators.


656-668: Select usage for group selection
Generates a minimal UI for group picking. Consider an advanced grouping or search mechanism if the list grows large.

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

16-48: Consider enhancing accessibility attributes

The refactor to use forwardRef is a good improvement. Consider adding these accessibility enhancements:

 <select
   id={props.name}
   className={clsx(
     "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-black sm:text-sm sm:leading-6",
     label && "mt-1",
   )}
   disabled={props.disabled}
+  aria-invalid={error ? "true" : "false"}
+  aria-describedby={`${props.name}-error`}
   ref={ref}
   {...selectProps}
 >
apps/web/app/(app)/automation/rule/[ruleId]/page.tsx (1)

Line range hint 28-44: Consider adding error boundary for rule processing

While the error handling for missing rules is good, consider wrapping the rule processing logic in an error boundary to gracefully handle any potential issues with the getConditions function or rule data transformation.

// Example error boundary component
class RuleProcessingErrorBoundary extends React.Component {
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Error processing rule. Please try again.</div>;
    }
    return this.props.children;
  }
}
apps/web/utils/condition.ts (1)

60-83: Consider optimizing getEmptyConditions

The function returns all condition types by default, which might lead to unnecessary data initialization. Consider:

  1. Making it accept a rule type parameter to return only relevant conditions
  2. Using a factory pattern for better maintainability

Example implementation:

export function getEmptyConditions(ruleType?: RuleType): ZodCondition[] {
  const conditionMap: Record<RuleType, ZodCondition> = {
    [RuleType.AI]: {
      type: RuleType.AI,
      instructions: "",
    },
    // ... other conditions
  };
  
  return ruleType ? [conditionMap[ruleType]] : Object.values(conditionMap);
}
apps/web/app/(app)/admin/AdminUpgradeUserForm.tsx (1)

59-59: LGTM! Consider standardizing Input registration style

The changes to form field registration look good and align with the standardization effort across the application. For complete consistency, consider updating the Input components to use the spread operator pattern as well:

-registerProps={register("emailAccountsAccess", { valueAsNumber: true })}
+{...register("emailAccountsAccess", { valueAsNumber: true })}

Also applies to: 98-98

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

64-64: Address commented code

There are commented references to a body field. Since this is a WIP PR, please either:

  1. Implement the body field handling
  2. Remove the commented code
  3. Add a TODO comment explaining the plan for this field

Also applies to: 130-130

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c9cdf8f and 5471ae8.

📒 Files selected for processing (18)
  • apps/web/app/(app)/admin/AdminUpgradeUserForm.tsx (2 hunks)
  • apps/web/app/(app)/automation/RuleForm.tsx (14 hunks)
  • apps/web/app/(app)/automation/group/CreateGroupModal.tsx (1 hunks)
  • apps/web/app/(app)/automation/group/ViewGroup.tsx (1 hunks)
  • apps/web/app/(app)/automation/rule/[ruleId]/page.tsx (2 hunks)
  • apps/web/app/(app)/automation/rule/create/page.tsx (2 hunks)
  • apps/web/app/(app)/bulk-archive/page.tsx (1 hunks)
  • apps/web/app/(app)/settings/EmailUpdatesSection.tsx (1 hunks)
  • apps/web/app/(app)/settings/ModelSection.tsx (1 hunks)
  • apps/web/app/(landing)/welcome/form.tsx (1 hunks)
  • apps/web/app/(landing)/welcome/survey.ts (1 hunks)
  • apps/web/components/Select.tsx (1 hunks)
  • apps/web/prisma/migrations/20241218123405_multi_conditions/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (3 hunks)
  • apps/web/utils/actions/rule.ts (5 hunks)
  • apps/web/utils/actions/validation.ts (1 hunks)
  • apps/web/utils/condition.ts (1 hunks)
  • apps/web/utils/llms/config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/app/(app)/automation/group/CreateGroupModal.tsx
🔇 Additional comments (37)
apps/web/app/(landing)/welcome/survey.ts (1)

Line range hint 1-1: Verify the PostHog attribution and licensing.

The comment indicates this code was copied from PostHog. Please ensure we have the appropriate permissions and licensing to use this code.

apps/web/app/(app)/automation/group/ViewGroup.tsx (1)

Line range hint 1-1: Verify the relationship with PR objectives

This file contains changes to form field registration methods but doesn't appear to implement the onboarding modal mentioned in the PR objectives. Could you clarify how this change relates to the loading onboarding modal implementation?

apps/web/app/(landing)/welcome/form.tsx (2)

Line range hint 1-256: Overall implementation looks good!

The OnboardingForm component is well-implemented with proper:

  • Error handling and form validation
  • Type safety throughout the component
  • Analytics integration with PostHog
  • Support for multiple question types
  • Clean separation of concerns

The changes to the routing logic align with the PR objectives of implementing the onboarding modal feature.


Line range hint 86-91: Verify the existence and implementation of routing paths

The A/B test routing logic looks correct, but we should verify that both destination paths ("/onboarding" and "/welcome-upgrade") are properly implemented.

✅ Verification successful

Both routing paths are properly implemented and consistently used

The verification confirms:

  • Both routes exist in the Next.js app directory structure:
    • /onboarding at apps/web/app/(app)/onboarding/
    • /welcome-upgrade at apps/web/app/(landing)/welcome-upgrade/
  • The routing paths are consistently used across the codebase:
    • In premium redirects (check-and-redirect-for-upgrade.tsx)
    • In the welcome form (welcome/form.tsx)
    • In the side navigation (SideNav.tsx)
    • In the onboarding finish component (OnboardingFinish.tsx)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the routing paths in the Next.js app directory structure

# Check for the existence of the onboarding and welcome-upgrade route directories
echo "Checking route implementations..."
fd -t d "onboarding$|welcome-upgrade$" "apps/web/app"

# Check for any references to these routes in the codebase
echo "Checking route usage..."
rg -A 3 "('|\"/)(onboarding|welcome-upgrade)('|\")" "apps/web"

Length of output: 1552

apps/web/utils/llms/config.ts (1)

Line range hint 1-39: Verify the relationship with onboarding modal

The changes in this file seem to be focused on LLM provider configuration, but the PR's main objective is about implementing an onboarding modal. Let's verify how these changes relate to the loading modal functionality.

apps/web/utils/actions/validation.ts (6)

57-57: New enum value added to RuleType: CATEGORY
No issues found here. This newly introduced enum value seems consistent with the overall design.


60-64: zodAiCondition correctness
Creates a dedicated shape for AI-based rules. Looks fine and consistent with the rest of the code.


65-69: zodGroupCondition scope
The schema here ensures the presence of a valid groupId. Consider ensuring that edge cases (empty or non-existent group) are handled upstream.


70-77: zodStaticCondition usage
The condition fields (to, from, subject, body) are all nullable. Double-check downstream code to ensure it handles null or undefined properly.


78-85: zodCategoryCondition structure
This condition captures category-based filtering. Everything looks consistent and clear.


86-92: Union of conditions
Combining multiple condition types in one union is a solid approach. Good job on ensuring future extensibility.

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

433-433: New RuleType 'CATEGORY'
Expanding the enum to include CATEGORY is coherent with the rest of the changes.


447-450: New LogicalOperator enum
Adding AND/OR at the schema level is sensible for complex rule conditions. Make sure your code fully leverages the new logic.

apps/web/app/(app)/automation/RuleForm.tsx (10)

3-3: useMemo import
Good to see you using React’s optimization hooks for memoizing data. No issues.


58-59: Imported getEmptyConditions & AlertError
Adding these imports indicates you’re handling condition-based logic in a more modular fashion. Appears fine.


75-82: useFieldArray initialization for conditions
This approach is well-suited for dynamic condition arrays. Watch out for interactions with the uniqueness logic in the createRuleBody schema.


Line range hint 109-145: updateRuleAction vs. createRuleAction
Properly separated logic for update vs. create. The success handling captures analytics events. Looks properly implemented.


155-164: Checking for unusedCondition
We see you prefer not to duplicate condition types. This logic pairs nicely with the refine check in the schema.


168-170: Trigger on conditions array changes
Ensures immediate revalidation. Watch for performance overhead if the form expands significantly.


185-194: AlertError for conditions
Excellent user feedback pattern for condition-level errors, especially the unique-type constraint.


386-397: Add Condition button
Neatly enables adding a new condition if one is available. Straightforward logic.


Line range hint 406-484: Actions building
• The toggling between AI-generated and static input is a nice user-experience touch.
• Using hasVariables to highlight template placeholders is effective.
• Keep an eye on possible template injection or XSS concerns if you enable advanced placeholders.


678-678: View button
Simple, direct approach to navigate or display the group. Everything looks in order.

apps/web/prisma/migrations/20241218123405_multi_conditions/migration.sql (3)

1-3: Create LogicalOperator enum
Adding a brand-new enum in PostgreSQL is straightforward. Ensuring backward compatibility for existing data likely isn’t an issue here.


4-5: Alter existing RuleType enum with 'CATEGORY'
Non-breaking addition for PostgreSQL enumerations. Keep in mind removing values later is more complicated.


7-9: Update Rule table
• The new column “typeLogic” set to default 'AND' is a safe approach.
• Dropping NOT NULL on “instructions” increases flexibility. Validate that your code can handle null instructions gracefully.

apps/web/app/(app)/automation/rule/create/page.tsx (1)

3-3: Verify the implementation of getConditions utility

The extraction of condition logic to a separate utility function improves modularity. However, we should verify the implementation of getConditions to ensure it handles all edge cases.

Consider adding TypeScript type annotations for the return type of getConditions to make the contract more explicit.

Also applies to: 22-25

✅ Verification successful

Let me try to search for the implementation with corrected commands.


Let me check the implementation of getConditions function.


Implementation of getConditions is well-structured and type-safe

The getConditions utility is properly implemented with:

  • Strong TypeScript typing using Prisma types and custom types
  • Comprehensive handling of all rule types (AI, GROUP, STATIC, CATEGORY)
  • Clear conditional logic for each rule type
  • Proper null/undefined handling
  • Consistent with the validation schema (CreateRuleBody)

The function is used correctly in both create and edit rule pages, and its implementation aligns with the architectural goals of the refactor.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation and tests of getConditions
echo "Searching for getConditions implementation:"
ast-grep --pattern 'export function getConditions' -l

echo "\nSearching for getConditions tests:"
fd "condition.test" --exec cat {}

Length of output: 377


Script:

#!/bin/bash
# Search for getConditions implementation with proper language specified
echo "Searching for getConditions implementation:"
ast-grep --pattern 'export function getConditions' --lang typescript

echo "\nSearching for getConditions usage:"
rg "getConditions" -A 3

echo "\nSearching for condition utility files:"
fd "condition" -e ts -e tsx

Length of output: 2229


Script:

#!/bin/bash
# Get the implementation of getConditions
echo "Content of condition utility file:"
cat apps/web/utils/condition.ts

Length of output: 2863

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

6-13: Props interface improvements look good

The extension of React's select attributes and explicit typing of options improve type safety and component reusability.

apps/web/app/(app)/automation/rule/[ruleId]/page.tsx (1)

7-7: Consistent condition handling implementation

The use of getConditions is consistent with the create page implementation, which is good for maintainability.

Also applies to: 44-44

apps/web/app/(app)/bulk-archive/page.tsx (1)

71-71: LGTM! Form registration refactor looks good.

The change to use spread operator with register is a cleaner approach and aligns with React Hook Form best practices.

apps/web/app/(app)/settings/EmailUpdatesSection.tsx (1)

97-97: LGTM! Consistent form registration pattern

The form registration change matches the pattern used across other components.

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

85-94: LGTM! Well-structured type definition

The FlattenedConditions type is well-defined and properly captures all possible condition properties.

apps/web/app/(app)/settings/ModelSection.tsx (2)

128-128: LGTM! Form field registration changes look good

The changes to use spread operator for Select components are consistent with the application-wide standardization.

Also applies to: 135-135


128-135: Verify model selection behavior

Please verify that the model selection logic still works correctly when changing providers. The useEffect hook depends on these fields, and we should ensure the new registration method maintains proper reactivity.

✅ Verification successful

Model selection behavior verified and working correctly

The code shows proper model selection handling:

  • The component watches both aiProvider and aiModel fields
  • When provider changes, it validates if the current model is compatible with the new provider
  • If the model is incompatible, it automatically sets the default model for the selected provider
  • Additional validation is in place for OpenAI provider to check API key validity
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other components that might be affected by this pattern
# Look for useEffect hooks watching aiProvider/aiModel

# Search for related useEffect dependencies
rg -A 5 "useEffect.*\[.*aiProvider" 

# Search for model selection logic
ast-grep --pattern 'useEffect(() => {
  $$$
  watch("aiModel")
  $$$
}, [$$$])'

Length of output: 119


Script:

#!/bin/bash
# Let's try a broader search to find model selection and provider-related logic

# Search for useEffect with aiProvider or aiModel
rg -A 10 "useEffect.*\[.*ai(Provider|Model)" 

# Search for form watch usage related to these fields
rg -A 5 "watch\(['\"]ai(Provider|Model)['\"]"

# Look for the ModelSection component implementation
rg -A 20 "ModelSection.*= .*\(" 

# Search for any form handling related to these fields
ast-grep --pattern 'const { register, watch } = useForm'

Length of output: 1391

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

21-21: LGTM! Condition flattening implementation

The addition of flattenConditions improves the code organization by centralizing condition processing logic.

Also applies to: 32-33


61-73: Verify condition handling changes

The changes to condition handling are significant. Please ensure:

  1. All condition types are properly handled
  2. The categoryFilters connection logic works as expected
  3. No conditions are lost during flattening

Also applies to: 127-140

✅ Verification successful

Let me gather more information about the condition handling implementation to verify all aspects thoroughly.


Based on the gathered information, I can now provide a final response as the code analysis shows a complete picture of the condition handling implementation.

Condition handling changes are properly implemented

The changes to condition handling are verified to be correct because:

  1. All condition types (AI, GROUP, STATIC, CATEGORY) are properly handled in the flattenConditions function with clear type definitions
  2. The categoryFilters connection logic correctly maps category IDs to Prisma's connect syntax
  3. No conditions are lost during flattening as the reducer properly accumulates all fields based on condition type

The implementation in condition.ts shows that the changes maintain data integrity and proper type safety through the entire flow.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify condition handling implementation

# Check for condition types usage
rg -A 5 "categoryFilterType.*=.*" 

# Look for related test coverage
rg -A 10 "describe.*Rule.*condition"

# Check for potential condition handling in other files
ast-grep --pattern 'flattenConditions($$$)'

Length of output: 1682


Script:

#!/bin/bash
# Check the implementation of flattenConditions
cat apps/web/utils/condition.ts

# Look for condition types and interfaces
rg -A 5 "interface.*Condition"
rg -A 5 "type.*Condition"

# Check for category filter related types/enums
rg -A 3 "enum.*Category"

# Look for tests related to category filters
rg -A 10 "test.*category.*filter"

Length of output: 4725


Line range hint 101-140: Review transaction atomicity

The transaction handling looks good, but please verify that all operations (rule update, action updates, and category filter updates) maintain consistency if any operation fails.

✅ Verification successful

Based on the code analysis, I can now provide a final response regarding the transaction atomicity concern:

Transaction atomicity is properly maintained

The transaction in the rule update operation is well-structured and maintains consistency:

  • All related operations (rule update, action deletions, updates, and creations) are wrapped in a single prisma.$transaction array
  • Error handling is implemented with a try-catch block that handles specific cases (duplicate names, group IDs) and generic errors
  • The transaction ensures all operations either complete together or roll back together
  • Category filter updates are part of the same atomic transaction through the set operation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check transaction handling patterns

# Look for similar transaction patterns
rg -B 5 -A 5 "\$transaction\("

# Check for error handling in transactions
ast-grep --pattern 'prisma.$transaction([
  $$$
  catch (error) {
    $$$
  }
])'

Length of output: 115


Script:

#!/bin/bash
# Let's try a different approach to find transaction patterns and error handling

# Search for transaction patterns with more context
rg -B 10 -A 10 "\$transaction\(" --type ts

# Look for try-catch blocks around transactions
rg -B 5 -A 10 "try\s*{.*?\$transaction" -U --multiline --type ts

# Search for error handling patterns specifically in rule.ts
fd "rule.ts" --exec cat {}

Length of output: 45313

Comment on lines +91 to 93
{...register("statsEmailFrequency")}
error={errors.statsEmailFrequency}
/> */}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Remove the commented-out stats email frequency section

The stats email frequency feature is actively used in the codebase:

  • It's defined in the Prisma schema with a default WEEKLY setting
  • Has validation rules in place
  • Has a functional API endpoint for updates
  • Is part of the user settings data model

The commented-out code should be removed rather than kept as a comment since:

  1. The feature is implemented and active in the backend
  2. Keeping commented code without explanation adds confusion
  3. The git history will preserve the code if needed
🔗 Analysis chain

Review commented-out stats email frequency section

The stats email frequency select is commented out. Since this PR is WIP, please clarify if this section should be:

  1. Removed completely
  2. Uncommented for the final version
  3. Kept commented out with a TODO explaining why

This could impact the user onboarding experience if stats emails are part of it.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if stats email frequency is used elsewhere
rg -l "statsEmailFrequency" --type typescript

Length of output: 80


Script:

#!/bin/bash
# Check if stats email frequency is used elsewhere, using ripgrep without file type filter
rg -l "statsEmailFrequency"

# Also search for similar terms to ensure we don't miss any references
rg -l "stats.*email"

Length of output: 686


Script:

#!/bin/bash
# Check the schema definition and related code to understand the feature's implementation
cat apps/web/prisma/schema.prisma | grep -A 5 -B 5 statsEmailFrequency

# Check the validation rules
cat apps/web/app/api/user/settings/email-updates/validation.ts

# Check the API route implementation
cat apps/web/app/api/user/settings/email-updates/route.ts

Length of output: 2165

Comment on lines 6 to 58
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation to getConditions

The function could benefit from input validation to ensure data integrity:

  1. Validate required fields based on rule type
  2. Add type guards for null checks
  3. Consider adding error handling for invalid combinations
function validateRuleInput(rule: Partial<Rule>): void {
  if (rule.type === RuleType.AI && !rule.instructions) {
    throw new Error("AI rule requires instructions");
  }
  // Add other validations
}

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 (2)
apps/web/utils/condition.ts (2)

59-81: Consider using TypeScript const assertions for empty conditions

The empty condition objects could benefit from stronger typing.

-const aiEmptyCondition = {
+const aiEmptyCondition = {
   type: RuleType.AI,
   instructions: "",
-};
+} as const;

113-122: Consider making FlattenedConditions properties more strict

The type definition allows too many nullable fields which could lead to runtime errors.

Consider making required fields non-nullable based on the condition type:

type FlattenedConditions = {
  // AI condition
  instructions: string | null;
  // GROUP condition
  groupId: string | null;
  // STATIC condition
  from: string | null;
  to: string | null;
  subject: string | null;
  body: string | null;
  // CATEGORY condition
  categoryFilterType: CategoryFilterType | null;
  categoryFilters: string[] | null;
} & {
  [K in keyof FlattenedConditions]?: FlattenedConditions[K];
};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5471ae8 and 80a8d96.

📒 Files selected for processing (2)
  • apps/web/app/(app)/automation/rule/create/page.tsx (2 hunks)
  • apps/web/utils/condition.ts (1 hunks)
🔇 Additional comments (3)
apps/web/app/(app)/automation/rule/create/page.tsx (2)

3-3: LGTM: Clean import of condition utilities

The new import statement properly brings in the required utility functions for condition handling.


22-24: Verify handling of undefined searchParams

The condition initialization looks correct, but we should ensure proper handling of undefined searchParams.

Consider adding type narrowing for better type safety:

-conditions: searchParams.tab
-  ? [getEmptyCondition(searchParams.tab, searchParams.groupId)]
-  : [],
+conditions: searchParams.tab && typeof searchParams.tab === 'string'
+  ? [getEmptyCondition(searchParams.tab, searchParams.groupId)]
+  : [],
apps/web/utils/condition.ts (1)

124-148: ** Add default case and improve type safety in flattenConditions**

A previous review already identified the need for exhaustive type checking in the switch statement. This issue still needs to be addressed.

Comment on lines 6 to 57
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for empty/invalid conditions

The getConditions function handles condition creation well, but lacks validation for empty or invalid conditions.

Consider adding validation:

 export function getConditions(
   rule: Partial<...>,
 ) {
+  if (!Object.keys(rule).some(key => rule[key as keyof typeof rule])) {
+    throw new Error('At least one condition must be specified');
+  }
   const conditions: CreateRuleBody["conditions"] = [];
   // ... rest of the function

Committable suggestion skipped: line range outside the PR's diff.

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

♻️ Duplicate comments (2)
apps/web/utils/condition.ts (2)

21-57: ⚠️ Potential issue

Add input validation to getConditions

The function needs validation for required fields and null checks:

  1. Validate that required fields are present for each condition type
  2. Add null check before accessing categoryFilters.map()
 export function getConditions(rule: RuleConditions) {
+  const validateField = (field: unknown, fieldName: string) => {
+    if (field === undefined || field === null || field === '') {
+      throw new Error(`${fieldName} is required`);
+    }
+  };
+
   const conditions: CreateRuleBody["conditions"] = [];

   if (rule.instructions) {
+    validateField(rule.instructions, 'instructions');
     conditions.push({
       type: RuleType.AI,
       instructions: rule.instructions,
     });
   }

   // ... similar for other conditions

   if (rule.categoryFilterType) {
+    validateField(rule.categoryFilterType, 'categoryFilterType');
     conditions.push({
       type: RuleType.CATEGORY,
       categoryFilterType: rule.categoryFilterType,
-      categoryFilters: rule.categoryFilters?.map((category) => category.id),
+      categoryFilters: rule.categoryFilters?.length 
+        ? rule.categoryFilters.map((category) => category.id)
+        : null,
     });
   }

   return conditions;
 }

92-111: ⚠️ Potential issue

Improve type safety in getEmptyCondition

The function should use the RuleType enum instead of string to ensure type safety at compile time.

 export function getEmptyCondition(
-  type: string,
+  type: RuleType,
   groupId?: string,
 ): ZodCondition {
🧹 Nitpick comments (3)
apps/web/utils/condition.ts (1)

173-203: Simplify string building in conditionsToString

The current string building logic with multiple if conditions could be simplified using array methods.

 export function conditionsToString(
   rule: RuleConditions & { group?: { name: string } | null },
 ) {
-  let result = "";
-
-  if (rule.groupId) {
-    result += `Group: ${rule.group?.name || "MISSING"}`;
-  }
-
-  if (rule.from || rule.to || rule.subject || rule.body) {
-    const from = rule.from ? `From: "${rule.from}"` : "";
-    if (from && result) result += " AND ";
-    result += from;
-
-    const subject = rule.subject ? `Subject: "${rule.subject}"` : "";
-    if (subject && result) result += " AND ";
-    result += subject;
-  }
-
-  if (rule.instructions) {
-    if (result) result += " AND ";
-    result += `AI: ${rule.instructions}`;
-  }
-
-  if (rule.categoryFilterType) {
-    if (result) result += " AND ";
-    result += `Category: ${rule.categoryFilterType} ${rule.categoryFilters?.join(", ")}`;
-  }
-
-  return result;
+  const parts = [
+    rule.groupId && `Group: ${rule.group?.name || "MISSING"}`,
+    rule.from && `From: "${rule.from}"`,
+    rule.subject && `Subject: "${rule.subject}"`,
+    rule.instructions && `AI: ${rule.instructions}`,
+    rule.categoryFilterType && 
+      `Category: ${rule.categoryFilterType} ${rule.categoryFilters?.join(", ")}`,
+  ].filter(Boolean);
+
+  return parts.join(" AND ");
 }
apps/web/app/(app)/automation/Rules.tsx (2)

148-150: LGTM! Good information hierarchy with security and type display.

The combination of SecurityAlert and condition types provides a clear visual hierarchy of rule information. Consider adding hover tooltips to explain the condition types for better user understanding.

Add tooltips to explain the condition types:

 <div className="flex items-center gap-1.5">
   <SecurityAlert rule={rule} />
-  {conditionTypesToString(rule)}
+  <Tooltip content="Explains the types of conditions applied in this rule">
+    <span>{conditionTypesToString(rule)}</span>
+  </Tooltip>
 </div>

Line range hint 1-324: Consider implementing a dedicated RuleDisplay component.

The rule display logic is becoming more complex with security alerts, conditions, and types. Consider extracting this into a dedicated RuleDisplay component to improve maintainability and reusability.

This would help:

  1. Centralize the display logic for rules
  2. Make it easier to test different display scenarios
  3. Facilitate future enhancements to rule visualization
  4. Reduce the complexity of the main Rules component
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 80a8d96 and d52a886.

📒 Files selected for processing (4)
  • apps/web/app/(app)/automation/ExecutedRulesTable.tsx (2 hunks)
  • apps/web/app/(app)/automation/Rules.tsx (2 hunks)
  • apps/web/utils/condition.ts (1 hunks)
  • apps/web/utils/rule.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/utils/rule.ts
🔇 Additional comments (5)
apps/web/app/(app)/automation/ExecutedRulesTable.tsx (2)

23-23: LGTM: Import change aligns with the refactoring.

The replacement of getInstructions with conditionsToString is consistent with the described changes.


79-79: Verify the new rule condition display format.

The switch to conditionsToString changes how rule conditions are presented to users. Since this is part of an onboarding-related PR, please ensure that:

  1. The new format is more user-friendly for onboarding
  2. All rule types are handled correctly
✅ Verification successful

Let me gather more information about the implementation of conditionsToString and the rule types it handles.


After reviewing the implementation of conditionsToString in condition.ts, I can now generate the final response:

The new rule condition display format is properly implemented

The conditionsToString function correctly handles all rule types defined in the system:

  • AI rules (instructions)
  • Group rules (group name)
  • Static rules (from, to, subject, body)
  • Category rules (categoryFilterType and filters)

The function creates a user-friendly string representation by:

  1. Properly joining multiple conditions with "AND"
  2. Including descriptive labels (e.g., "From:", "Subject:", "AI:")
  3. Handling null/undefined values gracefully
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if conditionsToString handles all rule types correctly
ast-grep --pattern 'export type Rule = {
  type: $_
}'

# Find all usages to verify consistent implementation
rg "conditionsToString" -A 3

Length of output: 1815


Script:

#!/bin/bash
# Check the implementation of conditionsToString
cat apps/web/utils/condition.ts

# Find rule type definitions
ast-grep --pattern 'type RuleConditions = {
  $$$
}'

# Look for any other rule-related type definitions
rg "type.*Rule" --type typescript

Length of output: 4917

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

124-148: 🛠️ Refactor suggestion

Add exhaustive type checking to flattenConditions

The switch statement should include exhaustive type checking like in ruleTypeToString.

 export const flattenConditions = (
   conditions: ZodCondition[],
 ): FlattenedConditions => {
   return conditions.reduce((acc, condition) => {
     switch (condition.type) {
       case RuleType.AI:
         acc.instructions = condition.instructions;
         break;
       case RuleType.GROUP:
         acc.groupId = condition.groupId;
         break;
       case RuleType.STATIC:
         acc.to = condition.to;
         acc.from = condition.from;
         acc.subject = condition.subject;
         acc.body = condition.body;
         break;
       case RuleType.CATEGORY:
         acc.categoryFilterType = condition.categoryFilterType;
         acc.categoryFilters = condition.categoryFilters;
         break;
+      default:
+        const _exhaustiveCheck: never = condition.type;
+        throw new Error(`Unhandled condition type: ${_exhaustiveCheck}`);
     }
     return acc;
   }, {} as FlattenedConditions);
 };

Likely invalid or redundant comment.

apps/web/app/(app)/automation/Rules.tsx (2)

44-44: LGTM! Import changes align with new condition-based display approach.

The new condition-related utility imports provide more specific and granular functionality for displaying rule conditions and types.


144-144: Verify the readability of the new condition display format.

The switch from instructions to conditions represents a significant change in how rules are presented to users.

Let's check the implementation of the new display function:

✅ Verification successful

The new condition display format is well-structured and readable

The conditionsToString implementation provides a clear, human-readable format that:

  • Properly formats group conditions as "Group: [name]"
  • Formats static conditions with clear labels (e.g., "From: [value]", "Subject: [value]")
  • Includes AI instructions prefixed with "AI: "
  • Formats category filters with type and values
  • Uses "AND" to clearly separate multiple conditions

The function handles all rule condition types and presents them in a consistent, user-friendly format that improves upon the previous implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of conditionsToString
# to ensure it provides clear and readable output

# Search for the conditionsToString implementation
ast-grep --pattern 'export function conditionsToString'

# Look for any tests that verify the output format
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx | xargs rg 'conditionsToString'

Length of output: 138


Script:

#!/bin/bash
# Let's try a broader search for conditionsToString implementation
rg -A 10 "conditionsToString" 

# Also search for any related condition formatting functions
rg -A 10 "formatCondition|formatRule|ruleCondition"

# Look for the component where this is being used
rg -A 5 -B 5 "{conditionsToString\(rule\)}"

Length of output: 4547


Script:

#!/bin/bash
# Let's examine the full implementation of conditionsToString
cat apps/web/utils/condition.ts

# Also look for the RuleConditions type definition
rg -A 5 "type RuleConditions"

# Check the surrounding context of where it's used in the Rules component
rg -A 10 -B 10 "TableCell.*conditionsToString" apps/web/app/\(app\)/automation/Rules.tsx

Length of output: 5181

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

♻️ Duplicate comments (3)
apps/web/utils/condition.ts (3)

23-59: 🛠️ Refactor suggestion

Add input validation to getConditions function

The function should validate inputs before processing:

  1. Validate required fields based on rule type
  2. Add validation for empty categoryFilters array
  3. Consider adding error handling for invalid combinations
 export function getConditions(rule: RuleConditions) {
+  // Validate required fields
+  if (rule.categoryFilterType && (!rule.categoryFilters || rule.categoryFilters.length === 0)) {
+    throw new Error("Category filters are required when categoryFilterType is set");
+  }
+
   const conditions: CreateRuleBody["conditions"] = [];
   // ... rest of the function

94-113: 🛠️ Refactor suggestion

Improve type safety in getEmptyCondition

The function accepts a string type parameter but should be constrained to valid RuleTypes.

 export function getEmptyCondition(
-  type: string,
+  type: RuleType,
   groupId?: string,
 ): ZodCondition {

126-150: 🛠️ Refactor suggestion

Add default case and improve type safety in flattenConditions

The switch statement should handle all possible cases and use exhaustive type checking.

 switch (condition.type) {
   // ... existing cases
+  default:
+    const _exhaustiveCheck: never = condition.type;
+    throw new Error(`Unhandled condition type: ${_exhaustiveCheck}`);
 }
🧹 Nitpick comments (4)
apps/web/app/api/user/rules/route.ts (1)

11-15: LGTM! Consider pagination for future scalability.

The expanded include clause properly fetches the necessary fields for category filters. This aligns well with the onboarding modal requirements.

Consider implementing pagination if the number of rules and their associated data could grow significantly. This would help maintain performance as users add more rules over time.

Example approach:

async function getRules(options: { 
  userId: string,
  skip?: number,
  take?: number
}) {
  return await prisma.rule.findMany({
    where: { userId: options.userId },
    include: {
      actions: true,
      group: { select: { name: true } },
      categoryFilters: { select: { id: true, name: true } },
    },
    orderBy: { createdAt: "asc" },
    skip: options.skip,
    take: options.take,
  });
}
apps/web/utils/condition.ts (3)

61-83: Consider making empty condition constants readonly

To prevent accidental modification of these constants, consider making them readonly.

-const aiEmptyCondition = {
+const aiEmptyCondition = Object.freeze({
   type: RuleType.AI,
   instructions: "",
-};
+});

-const groupEmptyCondition = {
+const groupEmptyCondition = Object.freeze({
   type: RuleType.GROUP,
   groupId: "",
-};
+});

175-212: Improve conditionsToString function readability and safety

The function could benefit from several improvements:

  1. Use template literals for better readability
  2. Add null checks for categoryFilters
  3. Split complex logic into smaller functions
 export function conditionsToString(rule: RuleConditions) {
+  const parts: string[] = [];
+
   const connector = rule.typeLogic === LogicalOperator.AND ? " AND " : " OR ";
 
   if (rule.groupId) {
-    result += `Group: ${rule.group?.name || "MISSING"}`;
+    parts.push(`Group: ${rule.group?.name || "MISSING"}`);
   }
 
   // ... similar changes for other conditions
 
-  return result;
+  return parts.join(connector);
 }

1-227: Add JSDoc documentation for public functions

Consider adding JSDoc documentation for all exported functions to improve maintainability and developer experience.

Example:

/**
 * Converts a rule's conditions into a human-readable string representation.
 * @param rule - The rule conditions to convert
 * @returns A string representation of the conditions, joined by AND/OR based on typeLogic
 */
export function conditionsToString(rule: RuleConditions): string {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d52a886 and 6da5a8a.

📒 Files selected for processing (3)
  • apps/web/app/api/user/rules/route.ts (1 hunks)
  • apps/web/utils/actions/rule.ts (5 hunks)
  • apps/web/utils/condition.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/actions/rule.ts
🔇 Additional comments (3)
apps/web/app/api/user/rules/route.ts (1)

Line range hint 6-6: Verify type safety across components.

The RulesResponse type definition ensures type safety for consuming components.

Let's verify how this type is being used across the codebase:

✅ Verification successful

Let me gather more information about the type safety by checking the actual implementations where RulesResponse is used.


Let me check one final aspect to verify the complete type safety by examining the actual type structure and its usage in components.


Type safety verification completed successfully

The RulesResponse type is properly defined and used consistently across the codebase:

  • It's correctly derived from the getRules function return type, which includes all necessary fields (actions, group, categoryFilters)
  • Components in the automation directory (Rules.tsx, ReportMistake.tsx) use proper type annotations
  • Type is used safely in utility functions (risk.ts, risk.test.ts) with proper array indexing
  • No concerning type assertions or unsafe type usage found related to this type
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of RulesResponse type and potential type mismatches

# Find files importing RulesResponse
rg -l "import.*RulesResponse" 

# Find potential usage of rule.categoryFilters
ast-grep --pattern 'rule.categoryFilters'

Length of output: 1917


Script:

#!/bin/bash
# Check the implementation and usage of RulesResponse type
rg -A 5 "RulesResponse" apps/web/utils/risk.ts apps/web/app/\(app\)/automation/Rules.tsx

# Check the getRules implementation to understand the type structure
ast-grep --pattern 'async function getRules'

# Check if there are any type errors or inconsistencies in the files using RulesResponse
rg "as RulesResponse|: RulesResponse"

Length of output: 3563


Script:

#!/bin/bash
# Check the actual type structure in the route file
cat apps/web/app/api/user/rules/route.ts

# Check for any type assertions or type-related issues
rg -A 2 "as any|as unknown" apps/web/app/\(app\)/automation/

Length of output: 1233

apps/web/utils/condition.ts (2)

1-21: LGTM! Well-structured type definitions

The type definitions are well-organized with proper use of TypeScript features like Pick and proper nullability handling.


158-227: LGTM! Well-implemented type conversion functions

The functions have proper exhaustive type checking and consistent error handling.

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

♻️ Duplicate comments (4)
apps/web/app/(app)/automation/RuleForm.tsx (3)

217-227: ⚠️ Potential issue

Remove redundant duplicate condition check

This issue was previously identified. The duplicate condition check is redundant as it's already handled by schema validation and could lead to race conditions.

- // check if we have duplicate condition types
- const conditionTypes = new Set(
-   conditions.map((condition) => condition.type),
- );
-
- if (conditionTypes.size !== conditions.length) {
-   toastError({
-     description:
-       "You can only have one condition of each type.",
-   });
- }

683-687: ⚠️ Potential issue

Implement error handling for group selection

The TODO comments indicate missing error handling for group selection. This should be implemented to ensure proper form validation.

- // TODO: fix this
- // error={props.errors.groupId}
- // error={
- //   errors.conditions?.[index]?.type as FieldError | undefined
- // }
+ error={
+   (props.errors.conditions?.[0] as { groupId?: FieldError })?.groupId
+ }

645-645: ⚠️ Potential issue

Fix potential race condition in group creation

Setting form values directly in the group creation callback could lead to race conditions if multiple group creations are triggered in quick succession.

Consider using a more robust state management approach as suggested in the previous review.

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

23-23: ⚠️ Potential issue

Add error handling for AI API calls

While the trace logging is a good addition, the function lacks proper error handling for potential AI API failures.

Consider wrapping the AI call in a try-catch block:

 async function getAiResponse(options: GetAiResponseOptions) {
   const { email, user, rules } = options;
   
   logger.trace("AI choose rule prompt", { system, prompt });

+  try {
     const aiResponse = await chatCompletionObject({
       userAi: user,
       prompt,
       system,
       schema: z.object({
         reason: z.string(),
         rule: z.number(),
       }),
       userEmail: user.email || "",
       usageLabel: "Choose rule",
     });

     logger.trace("AI choose rule response", aiResponse.object);
     return aiResponse.object;
+  } catch (error) {
+    logger.error("AI API call failed", { error, email: email.subject });
+    throw new Error("Failed to process rules with AI");
+  }
 }

Also applies to: 62-63, 76-77

🧹 Nitpick comments (9)
apps/web/components/ui/radio-group.tsx (1)

9-21: Consider adding ARIA role and label props for better accessibility.

While the implementation is solid, consider enhancing accessibility by documenting the expected ARIA props that should be passed to this component.

Add JSDoc comments to document the expected accessibility props:

+/**
+ * A radio group component that allows users to select a single option from a list.
+ * @example
+ * <RadioGroup
+ *   aria-label="View density"
+ *   defaultValue="comfortable"
+ *   name="view-density"
+ * >
+ *   <RadioGroupItem value="comfortable" />
+ * </RadioGroup>
+ */
const RadioGroup = React.forwardRef<
apps/web/app/(app)/automation/RuleForm.tsx (3)

166-166: Remove debug console.log statement

Remove the debug console.log statement as it shouldn't be present in production code.

-  console.log("errors", errors);

Line range hint 110-150: Refactor duplicate error handling and analytics logic

The error handling and analytics tracking logic is duplicated between the update and create cases. Consider extracting this into a shared function.

+ const handleRuleResponse = (res: any, action: 'updated' | 'created') => {
+   if (isActionError(res)) {
+     console.error(res);
+     toastError({ description: res.error });
+     return false;
+   }
+   if (!res.rule) {
+     toastError({
+       description: `There was an error ${action === 'updated' ? 'updating' : 'creating'} the rule.`,
+     });
+     return false;
+   }
+   toastSuccess({ description: action === 'updated' ? 'Saved!' : 'Created!' });
+   posthog.capture(`User ${action} AI rule`, {
+     conditions: data.conditions.map((condition) => condition.type),
+     actions: data.actions.map((action) => action.type),
+     automate: data.automate,
+     runOnThreads: data.runOnThreads,
+   });
+   return true;
+ };

  if (data.id) {
    const res = await updateRuleAction({ ...data, id: data.id });
-   if (isActionError(res)) {
-     console.error(res);
-     toastError({ description: res.error });
-   } else if (!res.rule) {
-     toastError({
-       description: "There was an error updating the rule.",
-     });
-   } else {
-     toastSuccess({ description: "Saved!" });
-     posthog.capture("User updated AI rule", {
-       conditions: data.conditions.map((condition) => condition.type),
-       actions: data.actions.map((action) => action.type),
-       automate: data.automate,
-       runOnThreads: data.runOnThreads,
-     });
+   if (handleRuleResponse(res, 'updated')) {
      router.push("/automation?tab=rules");
    }
  } else {
    const res = await createRuleAction(data);
-   if (isActionError(res)) {
-     console.error(res);
-     toastError({ description: res.error });
-   } else if (!res.rule) {
-     toastError({
-       description: "There was an error creating the rule.",
-     });
-   } else {
-     toastSuccess({ description: "Created!" });
-     posthog.capture("User created AI rule", {
-       conditions: data.conditions.map((condition) => condition.type),
-       actions: data.actions.map((action) => action.type),
-       automate: data.automate,
-       runOnThreads: data.runOnThreads,
-     });
+   if (handleRuleResponse(res, 'created')) {
      router.replace(`/automation/rule/${res.rule.id}`);
      router.push("/automation?tab=rules");
    }
  }

425-426: Improve responsive grid layout

The current grid layout might not provide the best experience on smaller screens. Consider adjusting the grid columns for better mobile responsiveness.

- <div className="grid gap-4 sm:grid-cols-4">
- <div className="sm:col-span-1">
+ <div className="grid gap-4 grid-cols-1 md:grid-cols-4">
+ <div className="md:col-span-1">
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (1)

81-110: Consider caching AI responses during onboarding

Since this is part of an onboarding modal where users might be waiting, consider implementing caching for AI responses to improve the user experience and reduce API costs. This could be particularly useful if users need to retry or modify rules during the onboarding process.

apps/web/utils/condition.ts (3)

10-25: Add documentation for the RuleConditions type.

Consider adding JSDoc comments to document the purpose and usage of the RuleConditions type, especially since it's exported and extends the base Rule type with additional fields.

+/**
+ * Represents the conditions that can be applied to a rule.
+ * Extends the base Rule type with additional fields for group and category information.
+ */
 export type RuleConditions = Partial<
   Pick<
     Rule,
     | "groupId"

27-45: Enhance type guards with null checks.

The type guards could be more robust by explicitly checking for null/undefined values.

 export function isAIRule<T extends RuleConditions>(
   rule: T,
 ): rule is T & { instructions: string } {
-  return !!rule.instructions;
+  return typeof rule.instructions === 'string' && rule.instructions.length > 0;
 }

 export function isGroupRule<T extends RuleConditions>(
   rule: T,
 ): rule is T & { groupId: string } {
-  return !!rule.groupId;
+  return typeof rule.groupId === 'string' && rule.groupId.length > 0;
 }

162-186: Add exhaustive type checking to flattenConditions.

The switch statement should include a default case with exhaustive type checking.

 export const flattenConditions = (
   conditions: ZodCondition[],
 ): FlattenedConditions => {
   return conditions.reduce((acc, condition) => {
     switch (condition.type) {
       // ... existing cases ...
+      default:
+        const _exhaustiveCheck: never = condition.type;
+        throw new Error(`Unhandled condition type: ${_exhaustiveCheck}`);
     }
     return acc;
   }, {} as FlattenedConditions);
 };
apps/web/app/(app)/automation/Rules.tsx (1)

205-214: Address TODO comment and remove commented-out code.

The TODO comment and commented-out code about multiple condition handling should either be implemented or removed to maintain code cleanliness.

Would you like me to help implement the multiple condition handling feature or create a GitHub issue to track this task?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 67c71fc and 3ecbf70.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/web/app/(app)/automation/RuleForm.tsx (16 hunks)
  • apps/web/app/(app)/automation/Rules.tsx (5 hunks)
  • apps/web/app/(app)/automation/TestRules.tsx (10 hunks)
  • apps/web/components/ui/radio-group.tsx (1 hunks)
  • apps/web/package.json (3 hunks)
  • apps/web/utils/ai/choose-rule/ai-choose-rule.ts (4 hunks)
  • apps/web/utils/ai/choose-rule/match-rules.test.ts (1 hunks)
  • apps/web/utils/condition.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/app/(app)/automation/TestRules.tsx
  • apps/web/utils/ai/choose-rule/match-rules.test.ts
  • apps/web/package.json
🔇 Additional comments (9)
apps/web/components/ui/radio-group.tsx (2)

1-8: LGTM! Imports and client directive are properly organized.

The "use client" directive and imports are correctly structured, with all necessary dependencies included.


44-44: LGTM! Clean export statement.

The named exports are properly defined.

apps/web/utils/ai/choose-rule/ai-choose-rule.ts (3)

6-9: LGTM! Good logging practices.

The addition of scoped logging and proper type imports enhances code maintainability and debugging capabilities.


81-87: LGTM! Well-typed function signature.

The generic type constraint and proper use of TypeScript utility types ensure type safety and clear interface requirements.


90-110: ⚠️ Potential issue

Add bounds checking and use consistent logging

The function has several areas for improvement:

  1. Missing validation for rule index bounds
  2. Inconsistent use of logging (mixing console.warn with logger)

Apply these improvements:

 export async function aiChooseRule<
   T extends { instructions: string },
 >(options: {
   email: EmailForLLM;
   rules: T[];
   user: Pick<User, "email" | "about"> & UserAIFields;
 }) {
   const { email, rules, user } = options;

   if (!rules.length) return { reason: "No rules" };

   const aiResponse = await getAiResponse({
     email,
     rules,
     user,
   });

   const ruleNumber = aiResponse ? aiResponse.rule - 1 : undefined;
   if (typeof ruleNumber !== "number") {
-    console.warn("No rule selected");
+    logger.warn("No rule selected", { email: email.subject });
     return { reason: aiResponse?.reason };
   }

+  if (ruleNumber < 0 || ruleNumber >= rules.length) {
+    logger.error("Invalid rule index", { ruleNumber, rulesLength: rules.length });
+    return { reason: "Invalid rule selection" };
+  }

   const selectedRule = rules[ruleNumber];

   return {
     rule: selectedRule,
     reason: aiResponse?.reason,
   };
 }

Likely invalid or redundant comment.

apps/web/utils/condition.ts (3)

130-149: 🛠️ Refactor suggestion

Use RuleType enum instead of string type.

The function parameter should use the RuleType enum for better type safety.

 export function getEmptyCondition(
-  type: string,
+  type: RuleType,
   groupId?: string,
 ): ZodCondition {

Likely invalid or redundant comment.


97-119: 🛠️ Refactor suggestion

Prevent potential mutations of shared empty condition objects.

The empty condition objects are shared between functions, which could lead to unintended mutations.

-const aiEmptyCondition = {
+const getAiEmptyCondition = () => ({
   type: RuleType.AI,
   instructions: "",
-};
+});

Likely invalid or redundant comment.


47-95: 🛠️ Refactor suggestion

Add error handling for edge cases in getConditions.

The function should handle potential edge cases where the rule object might be invalid or incomplete.

 export function getConditions(rule: RuleConditions) {
+  if (!rule || typeof rule !== 'object') {
+    throw new Error('Invalid rule object provided');
+  }
   const conditions: CreateRuleBody["conditions"] = [];

   if (isAIRule(rule)) {
     conditions.push({

Likely invalid or redundant comment.

apps/web/app/(app)/automation/Rules.tsx (1)

143-148: LGTM! Proper usage of new utility functions.

The replacement of the old implementation with conditionsToString and conditionTypesToString is correct and improves code maintainability.

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

🧹 Nitpick comments (2)
apps/web/utils/condition.ts (1)

27-45: Consider strengthening type guards with additional validation

The type guards are well-implemented but could be more robust:

  1. isAIRule should validate instruction content
  2. isStaticRule could be more specific about valid patterns

Consider these improvements:

 export function isAIRule<T extends RuleConditions>(
   rule: T,
 ): rule is T & { instructions: string } {
-  return !!rule.instructions;
+  return !!rule.instructions?.trim();
 }

 export function isStaticRule(rule: RuleConditions) {
-  return !!rule.from || !!rule.to || !!rule.subject || !!rule.body;
+  return !!(
+    rule.from?.trim() ||
+    rule.to?.trim() ||
+    rule.subject?.trim() ||
+    rule.body?.trim()
+  );
 }
apps/web/app/(app)/automation/RuleForm.tsx (1)

347-383: Consider optimizing category loading performance

The category loading implementation could be optimized:

  1. Categories could be preloaded or cached
  2. Loading state could show a skeleton loader

Consider implementing React Suspense or skeleton loading:

function CategorySkeleton() {
  return (
    <div className="animate-pulse">
      <div className="h-8 w-full rounded bg-gray-200" />
      <div className="mt-2 grid grid-cols-3 gap-2">
        {[1, 2, 3].map((i) => (
          <div key={i} className="h-6 rounded bg-gray-200" />
        ))}
      </div>
    </div>
  );
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3ecbf70 and c037166.

📒 Files selected for processing (4)
  • apps/web/app/(app)/automation/RuleForm.tsx (15 hunks)
  • apps/web/utils/actions/rule.ts (5 hunks)
  • apps/web/utils/actions/validation.ts (2 hunks)
  • apps/web/utils/condition.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/actions/rule.ts
🔇 Additional comments (7)
apps/web/utils/actions/validation.ts (3)

57-57: LGTM: Category rule type addition

The addition of RuleType.CATEGORY to the zodRuleType enum is well-placed and consistent with the existing rule types.


98-109: LGTM: Robust conditions array validation

The conditions array validation is well implemented with:

  1. Minimum length requirement
  2. Type uniqueness check via refinement

135-135: LGTM: Simplified testAiBody schema

The removal of threadId from testAiBody schema aligns with the current implementation needs.

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

162-186: Add exhaustive type checking to flattenConditions

The switch statement in flattenConditions should handle all possible cases.

apps/web/app/(app)/automation/RuleForm.tsx (3)

215-225: Remove redundant duplicate condition check

The duplicate condition check is already handled by the schema validation.


647-649: Potential race condition in group creation

Setting form values directly in the group creation callback could lead to race conditions.


686-688: Implement error handling for group selection

The TODO comment indicates missing error handling for group selection.

Comment on lines +60 to +89
const zodAiCondition = z.object({
instructions: z.string().nullish(),
automate: z.boolean().nullish(),
runOnThreads: z.boolean().nullish(),
actions: z.array(zodAction),
type: zodRuleType,
// static conditions
from: z.string().nullish(),
});

const zodGroupCondition = z.object({
groupId: z.string().nullish(),
});

const zodStaticCondition = z.object({
to: z.string().nullish(),
from: z.string().nullish(),
subject: z.string().nullish(),
// body: z.string().nullish(), // not in use atm
// group
groupId: z.string().nullish(),
body: z.string().nullish(),
});

const zodCategoryCondition = z.object({
categoryFilterType: z
.enum([CategoryFilterType.INCLUDE, CategoryFilterType.EXCLUDE])
.nullish(),
categoryFilters: z.array(z.string()).nullish(),
});

const zodCondition = z.object({
type: zodRuleType,
...zodAiCondition.shape,
...zodGroupCondition.shape,
...zodStaticCondition.shape,
...zodCategoryCondition.shape,
});
export type ZodCondition = z.infer<typeof zodCondition>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider strengthening validation for condition schemas

While the condition schemas are well-structured, they could benefit from additional validation:

  1. The zodAiCondition schema allows nullish instructions which could lead to invalid AI rules
  2. The zodStaticCondition schema allows all fields to be nullish which could result in empty conditions

Consider this improvement:

 const zodAiCondition = z.object({
-  instructions: z.string().nullish(),
+  instructions: z.string().min(1, "Instructions are required for AI rules"),
 });

 const zodStaticCondition = z.object({
   to: z.string().nullish(),
   from: z.string().nullish(),
   subject: z.string().nullish(),
   body: z.string().nullish(),
-});
+}).refine(
+  (data) => Object.values(data).some((v) => v != null),
+  "At least one field must be specified"
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const zodAiCondition = z.object({
instructions: z.string().nullish(),
automate: z.boolean().nullish(),
runOnThreads: z.boolean().nullish(),
actions: z.array(zodAction),
type: zodRuleType,
// static conditions
from: z.string().nullish(),
});
const zodGroupCondition = z.object({
groupId: z.string().nullish(),
});
const zodStaticCondition = z.object({
to: z.string().nullish(),
from: z.string().nullish(),
subject: z.string().nullish(),
// body: z.string().nullish(), // not in use atm
// group
groupId: z.string().nullish(),
body: z.string().nullish(),
});
const zodCategoryCondition = z.object({
categoryFilterType: z
.enum([CategoryFilterType.INCLUDE, CategoryFilterType.EXCLUDE])
.nullish(),
categoryFilters: z.array(z.string()).nullish(),
});
const zodCondition = z.object({
type: zodRuleType,
...zodAiCondition.shape,
...zodGroupCondition.shape,
...zodStaticCondition.shape,
...zodCategoryCondition.shape,
});
export type ZodCondition = z.infer<typeof zodCondition>;
const zodAiCondition = z.object({
instructions: z.string().min(1, "Instructions are required for AI rules"),
});
const zodGroupCondition = z.object({
groupId: z.string().nullish(),
});
const zodStaticCondition = z.object({
to: z.string().nullish(),
from: z.string().nullish(),
subject: z.string().nullish(),
body: z.string().nullish(),
}).refine(
(data) => Object.values(data).some((v) => v != null),
"At least one field must be specified"
);
const zodCategoryCondition = z.object({
categoryFilterType: z
.enum([CategoryFilterType.INCLUDE, CategoryFilterType.EXCLUDE])
.nullish(),
categoryFilters: z.array(z.string()).nullish(),
});
const zodCondition = z.object({
type: zodRuleType,
...zodAiCondition.shape,
...zodGroupCondition.shape,
...zodStaticCondition.shape,
...zodCategoryCondition.shape,
});
export type ZodCondition = z.infer<typeof zodCondition>;

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

♻️ Duplicate comments (1)
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (1)

81-110: ⚠️ Potential issue

Add validation and error handling

The function needs additional safeguards:

  1. Validate rule index bounds
  2. Add error handling for AI response processing
  3. Improve logging for failure cases

Apply these improvements:

 export async function aiChooseRule<
   T extends { instructions: string },
 >(options: {
   email: EmailForLLM;
   rules: T[];
   user: Pick<User, "email" | "about"> & UserAIFields;
 }) {
   const { email, rules, user } = options;

   if (!rules.length) return { reason: "No rules" };

+  try {
     const aiResponse = await getAiResponse({
       email,
       rules,
       user,
     });

     const ruleNumber = aiResponse ? aiResponse.rule - 1 : undefined;
     if (typeof ruleNumber !== "number") {
-      console.warn("No rule selected");
+      logger.warn("No rule selected", { aiResponse });
       return { reason: aiResponse?.reason };
     }

+    if (ruleNumber < 0 || ruleNumber >= rules.length) {
+      logger.error("Invalid rule index", { ruleNumber, rulesLength: rules.length });
+      return { reason: "Selected rule index out of bounds" };
+    }

     const selectedRule = rules[ruleNumber];

     return {
       rule: selectedRule,
       reason: aiResponse?.reason,
     };
+  } catch (error) {
+    logger.error("Failed to choose rule", { error, email: email.subject });
+    return { reason: "Error processing rules" };
+  }
 }
🧹 Nitpick comments (6)
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (2)

Line range hint 49-61: Document AI response format expectations

Consider adding JSDoc comments to document the expected AI response format and its constraints. This will help other developers understand the contract between the code and the AI model.

+/**
+ * The AI prompt format for rule selection.
+ * Expected response format:
+ * {
+ *   reason: string; // Concise explanation for the rule selection
+ *   rule: number;   // 1-based index of the selected rule
+ * }
+ */
 const prompt = `An email was received for processing...

Line range hint 23-110: Consider adding rate limiting and caching

For production scalability, consider:

  1. Implementing rate limiting for AI calls
  2. Caching similar email-rule combinations
  3. Adding timeout handling for AI responses

Would you like me to provide a detailed implementation proposal for these improvements?

apps/web/package.json (1)

146-146: Consider updating testing documentation.

The addition of testing-related dependencies (cross-env and vitest-mock-extended) and the new test configuration suggest changes to the testing strategy.

Consider updating the project's testing documentation to include:

  • The distinction between regular and AI tests
  • How to use the enhanced mocking capabilities
  • When to run each type of test

Also applies to: 158-159

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

5-25: Consider performance optimization and documentation improvements

The implementation is thorough but could benefit from some improvements:

  1. Consider combining some regex operations to reduce string traversals
  2. Add JSDoc comments explaining the function's purpose and transformation steps
+/**
+ * Removes excessive whitespace and invisible characters from a string while preserving meaningful formatting.
+ * 
+ * Performs the following transformations:
+ * 1. Removes invisible characters (zero-width spaces, soft hyphens, etc.)
+ * 2. Normalizes line breaks to \n
+ * 3. Collapses multiple newlines to double newlines
+ * 4. Cleans up spaces around newlines
+ * 5. Replaces multiple spaces with single space
+ * 
+ * @param str - The input string to process
+ * @returns A cleaned string with normalized whitespace
+ */
 export function removeExcessiveWhitespace(str: string) {
   return (
     str
-      // First remove all zero-width spaces, soft hyphens, and other invisible characters
-      // Handle each special character separately to avoid combining character issues
       .replace(
         /\u200B|\u200C|\u200D|\u200E|\u200F|\uFEFF|\u3164|\u00AD|\u034F/g,
         " ",
       )
-      // Normalize all types of line breaks to \n
       .replace(/\r\n|\r/g, "\n")
-      // Then collapse multiple newlines (3 or more) into double newlines
       .replace(/\n\s*\n\s*\n+/g, "\n\n")
-      // Clean up spaces around newlines (but preserve double newlines)
       .replace(/[^\S\n]*\n[^\S\n]*/g, "\n")
-      // Replace multiple spaces (but not newlines) with single space
       .replace(/[^\S\n]+/g, " ")
-      // Clean up any trailing/leading whitespace
       .trim()
   );
 }
apps/web/utils/string.test.ts (1)

18-65: Add performance and boundary test cases

The test suite is comprehensive but could benefit from additional test cases:

  1. Test with very long strings to verify performance
  2. Test with strings at boundary conditions (e.g., maximum string length)

Add these test cases:

it("should handle very long strings efficiently", () => {
  const longString = " ".repeat(10000);
  const start = performance.now();
  removeExcessiveWhitespace(longString);
  const end = performance.now();
  expect(end - start).toBeLessThan(100); // should process within 100ms
});

it("should handle strings at maximum length", () => {
  const maxString = "a".repeat(String.prototype.length);
  expect(() => removeExcessiveWhitespace(maxString)).not.toThrow();
});
apps/web/utils/ai/choose-rule/stringify-email.ts (1)

17-17: Consider HTML content handling and type validation

While the whitespace removal improves content formatting, consider these enhancements:

  1. Add HTML content detection and handling
  2. Validate email field types and content
function isHTML(str: string): boolean {
  return /<[a-z][\s\S]*>/i.test(str);
}

// Usage in stringifyEmail:
`<body>${
  truncate(
    removeExcessiveWhitespace(
      isHTML(email.content) 
        ? stripHTMLTags(email.content) 
        : email.content
    ),
    maxLength
  )
}</body>`
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c037166 and b7729a6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/web/app/(app)/automation/ReportMistake.tsx (6 hunks)
  • apps/web/app/(app)/automation/TestRules.tsx (10 hunks)
  • apps/web/package.json (4 hunks)
  • apps/web/utils/ai/choose-rule/ai-choose-rule.ts (5 hunks)
  • apps/web/utils/ai/choose-rule/stringify-email.ts (2 hunks)
  • apps/web/utils/string.test.ts (1 hunks)
  • apps/web/utils/string.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/(app)/automation/TestRules.tsx
  • apps/web/app/(app)/automation/ReportMistake.tsx
🔇 Additional comments (6)
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (3)

6-9: LGTM! Good logging setup.

The addition of scoped logging is a good practice for debugging and tracing AI rule selection process.


62-63: Good addition of trace logging!

The trace logging for both prompt and response will be valuable for debugging AI behavior.

Also applies to: 76-77


Line range hint 23-79: Add error handling for AI completion calls

The function should handle potential errors from the chatCompletionObject call.

Apply this improvement:

 async function getAiResponse(options: GetAiResponseOptions) {
   const { email, user, rules } = options;
   
   // ... existing code ...

   logger.trace("AI choose rule prompt", { system, prompt });

+  try {
     const aiResponse = await chatCompletionObject({
       userAi: user,
       prompt,
       system,
       schema: z.object({
         reason: z.string(),
         rule: z.number(),
       }),
       userEmail: user.email || "",
       usageLabel: "Choose rule",
     });

     logger.trace("AI choose rule response", aiResponse.object);

     return aiResponse.object;
+  } catch (error) {
+    logger.error("Failed to get AI response", { error });
+    throw new Error("Failed to process rules with AI");
+  }
 }
apps/web/package.json (2)

10-11: LGTM! Good separation of test commands.

The split between regular and AI tests using environment variables is a good practice. This allows for faster regular test runs while maintaining the ability to run AI-specific tests when needed.


48-48: Verify usage of the radio-group component.

The addition of @radix-ui/react-radio-group suggests implementation of radio button selections in the onboarding modal.

apps/web/utils/ai/choose-rule/stringify-email.ts (1)

1-1: Verify alignment with PR objectives

The string manipulation utilities are well-implemented and tested, but they seem tangential to the PR's main objective of implementing a loading onboarding modal. Consider:

  1. Splitting these changes into a separate PR for better review focus
  2. Clarifying how these changes support the onboarding modal feature

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/ai/choose-rule/ai-choose-rule.ts (1)

Line range hint 23-79: Consider enhancing error logging

The function has good logging for the prompt and response, but could benefit from error logging when schema validation fails.

   const aiResponse = await chatCompletionObject({
     userAi: user,
     prompt,
     system,
     schema: z.object({
       reason: z.string(),
       rule: z.number(),
     }),
     userEmail: user.email || "",
     usageLabel: "Choose rule",
+  }).catch(error => {
+    logger.error("AI response failed", { error, email: email.subject });
+    throw error;
   });
apps/web/utils/ai/choose-rule/run-rules.ts (1)

19-21: Maintain consistent logging approach

The logger is commented out here while being actively used in ai-choose-rule.ts. Consider maintaining a consistent logging approach across files.

-// import { createScopedLogger } from "@/utils/logger";
-
-// const logger = createScopedLogger("ai-run-rules");
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("ai-run-rules");
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b7729a6 and d7c3198.

📒 Files selected for processing (3)
  • apps/web/app/(app)/automation/TestRules.tsx (12 hunks)
  • apps/web/utils/ai/choose-rule/ai-choose-rule.ts (4 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(app)/automation/TestRules.tsx
🔇 Additional comments (5)
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (2)

6-9: LGTM! Good logging setup

The addition of scoped logging and proper type imports enhances code maintainability and debugging capabilities.


81-110: Enhance error handling and bounds checking

The function needs better error handling and validation:

  1. No error handling for AI response failures
  2. No bounds checking for rule index
  3. Limited logging for debugging
apps/web/utils/ai/choose-rule/run-rules.ts (3)

42-60: Add error handling for findMatchingRule call

The findMatchingRule call could potentially throw an error, but there's no try-catch block to handle potential failures gracefully.


96-105: Fix potential race condition in rule execution

The check for shouldExecute and subsequent execution could have a race condition.


207-254: LGTM! Well-handled database operations

The function has good error handling for race conditions and proper logging for duplicate entries.

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

🔭 Outside diff range comments (2)
apps/web/app/(app)/automation/RuleForm.tsx (1)

Line range hint 99-109: Handle potential race conditions in label creation

The sequential label creation in a for loop could lead to race conditions. Consider using Promise.all for parallel execution and better error handling.

-for (const action of data.actions) {
-  if (action.type === ActionType.LABEL) {
-    const hasLabel = gmailLabelsData?.labels?.some(
-      (label) => label.name === action.label,
-    );
-    if (!hasLabel && action.label?.value && !action.label?.ai) {
-      await createLabelAction({ name: action.label.value });
-    }
-  }
-}
+await Promise.all(
+  data.actions
+    .filter(
+      (action) =>
+        action.type === ActionType.LABEL &&
+        !gmailLabelsData?.labels?.some((label) => label.name === action.label) &&
+        action.label?.value &&
+        !action.label?.ai
+    )
+    .map((action) => createLabelAction({ name: action.label.value }))
+);
apps/web/app/(app)/cold-email-blocker/TestRules.tsx (1)

Line range hint 89-142: Enhance error handling and form validation

The form submission logic could be improved in several ways:

  1. Add input validation for the message field beyond just required
  2. Implement proper error boundary for API errors
  3. Consider rate limiting for API calls

Consider implementing these improvements:

 const TestRulesForm = () => {
   const [coldEmailResponse, setColdEmailResponse] =
     useState<ColdEmailBlockerResponse | null>(null);
+  const [apiError, setApiError] = useState<string | null>(null);
 
   const {
     register,
     handleSubmit,
     formState: { errors, isSubmitting },
   } = useForm<TestRulesInputs>({
     defaultValues: {
       message:
         "Hey, I run a development agency. I was wondering if you need extra hands on your team?",
     },
+    validate: {
+      message: (value) => {
+        if (value.length < 10) return "Message must be at least 10 characters";
+        if (value.length > 1000) return "Message must not exceed 1000 characters";
+        return true;
+      }
+    }
   });
 
   const onSubmit: SubmitHandler<TestRulesInputs> = useCallback(async (data) => {
+    setApiError(null);
     try {
       const res = await postRequest<
         ColdEmailBlockerResponse,
         ColdEmailBlockerBody
       >("/api/ai/cold-email", {
         email: {
           from: "",
           subject: "",
           textHtml: null,
           textPlain: data.message,
           snippet: null,
           threadId: null,
         },
       });
 
       if (isError(res)) {
         console.error(res);
+        setApiError(res.error);
         toastError({
           title: "Error checking if cold email.",
           description: res.error,
         });
       } else {
         setColdEmailResponse(res);
       }
+    } catch (error) {
+      setApiError("An unexpected error occurred");
+      console.error(error);
     }
   }, []);
🧹 Nitpick comments (5)
apps/web/app/(app)/automation/RuleForm.tsx (2)

166-169: Consider debouncing the trigger effect

The current implementation triggers validation on every condition change, which could lead to performance issues with rapid changes. Consider debouncing the trigger call.

-useEffect(() => {
-  trigger("conditions");
-}, [conditions]);
+const debouncedTrigger = useMemo(
+  () => debounce(() => trigger("conditions"), 300),
+  [trigger]
+);
+
+useEffect(() => {
+  debouncedTrigger();
+  return () => debouncedTrigger.cancel();
+}, [conditions, debouncedTrigger]);

628-634: Remove or document commented code

The commented-out code for viewing examples should either be removed if it's no longer needed or documented with a TODO explaining why it's temporarily disabled.

-{/* {!isAIRule(rule) && (
-  <Button variant="outline" asChild>
-    <Link href={`/automation/rule/${rule.id}/examples`}>
-      View Examples
-    </Link>
-  </Button>
-)} */}
+// TODO: Example viewing is temporarily disabled until the AI rule detection is implemented
apps/web/app/(app)/cold-email-blocker/TestRules.tsx (3)

Line range hint 171-211: Implement debouncing for API calls in TestRulesContentRow

The test button click handler makes immediate API calls without debouncing, which could lead to rate limiting issues if clicked rapidly.

Consider implementing debouncing:

+import { useMemo } from "react";
+import debounce from "lodash/debounce";

 function TestRulesContentRow(props: {
   message: MessagesResponse["messages"][number];
   userEmail: string;
 }) {
   const { message } = props;
 
   const [loading, setLoading] = useState(false);
   const [coldEmailResponse, setColdEmailResponse] =
     useState<ColdEmailBlockerResponse | null>(null);
 
+  const debouncedTestEmail = useMemo(
+    () =>
+      debounce(async () => {
+        setLoading(true);
+        try {
+          const res = await postRequest<
+            ColdEmailBlockerResponse,
+            ColdEmailBlockerBody
+          >("/api/ai/cold-email", {
+            email: {
+              from: message.headers.from,
+              subject: message.headers.subject,
+              textHtml: message.textHtml || null,
+              textPlain: message.textPlain || null,
+              snippet: message.snippet || null,
+              threadId: message.threadId,
+            },
+          });
+
+          if (isError(res)) {
+            console.error(res);
+            toastError({
+              title: "Error checking whether it's a cold email.",
+              description: res.error,
+            });
+          } else {
+            setColdEmailResponse(res);
+          }
+        } finally {
+          setLoading(false);
+        }
+      }, 300),
+    [message]
+  );

Line range hint 44-85: Consider implementing pagination for message list

The TestRulesContent component fetches messages without pagination, which could lead to performance issues with large datasets.

Consider implementing:

  1. Pagination parameters in the API call
  2. Infinite scroll or load more button
  3. Virtual scrolling for large lists

Example implementation:

function TestRulesContent() {
  const [page, setPage] = useState(1);
  const { data, isLoading, error, size, setSize } = useSWRInfinite(
    (index) => `/api/google/messages?q=${searchQuery}&page=${index + 1}`,
    {
      keepPreviousData: true,
      dedupingInterval: 1_000,
    }
  );

  const messages = data ? data.flatMap(d => d.messages) : [];
  const isLoadingMore = isLoading || (size > 0 && data && typeof data[size - 1] === "undefined");
  const isEmpty = data?.[0]?.messages.length === 0;
  const isReachingEnd = isEmpty || (data && data[data.length - 1]?.messages.length < PAGE_SIZE);
}

Line range hint 223-251: Enhance Result component with more detailed feedback

The Result component could provide more detailed feedback to help users understand why an email was classified as cold or not.

Consider enhancing the component:

 function Result(props: { coldEmailResponse: ColdEmailBlockerResponse | null }) {
   const { coldEmailResponse } = props;
 
   if (!coldEmailResponse) return null;
 
+  const getDetailedFeedback = () => {
+    const details = [];
+    if (coldEmailResponse.hasUnsubscribeLink) {
+      details.push("Contains unsubscribe link");
+    }
+    if (coldEmailResponse.hasPreviousThread) {
+      details.push("Part of an existing thread");
+    }
+    return details.join(", ");
+  };
+
   if (coldEmailResponse.isColdEmail) {
     return (
       <AlertBasic
         variant="destructive"
         title="Email is a cold email!"
-        description={coldEmailResponse.aiReason}
+        description={`${coldEmailResponse.aiReason}\n${getDetailedFeedback()}`}
       />
     );
   }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d7c3198 and 237bd00.

📒 Files selected for processing (4)
  • apps/web/app/(app)/automation/RuleForm.tsx (15 hunks)
  • apps/web/app/(app)/automation/TestRules.tsx (13 hunks)
  • apps/web/app/(app)/cold-email-blocker/TestRules.tsx (1 hunks)
  • apps/web/app/(app)/cold-email-blocker/TestRulesMessage.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(app)/automation/TestRules.tsx
🔇 Additional comments (5)
apps/web/app/(app)/automation/RuleForm.tsx (3)

215-225: Remove redundant duplicate condition check

The duplicate condition check is already handled by the schema validation. The current implementation could lead to a race condition where the user sees an error toast before the form validation triggers.


714-715: Implement error handling for group selection

The TODO comments indicate missing error handling for group selection. This should be implemented to ensure proper form validation.


Line range hint 1-786: Verify error handling coverage

Let's verify that all API calls and user interactions have proper error handling implemented.

✅ Verification successful

Based on the shell script results, I can now provide a final response regarding the error handling verification in RuleForm.tsx:

Error handling is properly implemented throughout the form

The code demonstrates comprehensive error handling with:

  • Proper API error handling for all action calls (createLabelAction, updateRuleAction, createRuleAction)
  • User feedback through toast notifications for both success and error cases
  • Form validation errors displayed through the ErrorMessage component
  • Loading states tracked via isSubmitting
  • Error boundaries for API responses using isActionError checks

Key error handling patterns found:

  • Error handling for label creation: if (isActionError(res)) throw new Error(res.error)
  • Rule update errors: if (isActionError(res)) { toastError({ description: res.error }) }
  • Form validation errors: error={errors.name}
  • Group creation errors: if (isActionError(result)) { toastError({ description: result.error }) }
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for error handling coverage in API calls and user interactions

# Check for API calls without error handling
rg -A 2 "await.*Action\(" --type typescript

# Check for toast error usage
rg "toastError" --type typescript

# Check for error state handling in components
ast-grep --pattern 'error={$_}'

Length of output: 183


Script:

#!/bin/bash
# Let's try without file type restrictions since typescript extension wasn't recognized

# Check for API calls without error handling
rg -A 2 "await.*Action\("

# Check for toast error usage
rg "toastError"

# Check for error state handling in components
rg "error=\{" 

Length of output: 38397

apps/web/app/(app)/cold-email-blocker/TestRulesMessage.tsx (2)

9-21: LGTM: Props interface is well-structured

The props interface is clean and properly typed. The destructuring pattern improves readability and makes the component's requirements explicit.


6-6: Verify the impact of switching from search to direct message URL

The change from getGmailSearchUrl to getGmailUrl with messageId parameter is more precise as it links directly to the specific email. However, we should verify that the Gmail URL works correctly with the new parameter structure.

Also applies to: 28-28

✅ Verification successful

Let me check the implementation of both functions to ensure the URL generation is consistent.


Let me try a different approach to check the implementation using ripgrep with context.


URL generation change is correct and consistent

The change from getGmailSearchUrl to getGmailUrl is correct. The getGmailUrl function properly handles direct message links using the message ID, while getGmailSearchUrl is still maintained for search-based functionality. Both functions use the same base URL generation and are used appropriately throughout the codebase - direct message links for specific emails and search URLs for filtering by sender.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any other usages of getGmailSearchUrl to ensure consistent URL generation

# Search for any remaining uses of getGmailSearchUrl
rg "getGmailSearchUrl"

# Search for all uses of getGmailUrl to verify consistent usage
rg "getGmailUrl"

Length of output: 2626


Script:

#!/bin/bash
# Check the implementation of both URL generation functions
ast-grep --pattern 'export function getGmailUrl($_) { $$$ }'
ast-grep --pattern 'export function getGmailSearchUrl($_) { $$$ }'

Length of output: 131


Script:

#!/bin/bash
# Check the implementation of both URL generation functions with context
rg -A 10 "^export function getGmailUrl" apps/web/utils/url.ts
rg -A 10 "^export function getGmailSearchUrl" apps/web/utils/url.ts

Length of output: 843

@elie222 elie222 changed the title WIP: Loading onboarding modal Multi condition supports. Allow smart categories in rules Dec 21, 2024
@elie222 elie222 merged commit 7819ab8 into main Dec 21, 2024
1 check passed
@sentry
Copy link

sentry bot commented Dec 21, 2024

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

  • ‼️ Error: Invalid condition type: CATEGORYcategory=Account /automation/rule/create View Issue
  • ‼️ TypeError: Cannot read properties of undefined (reading 'fields') /automation/rule/cm5beqy9d0001cgj8l6n9zo97 View Issue

Did you find this useful? React with a 👍 or 👎

@elie222 elie222 deleted the loading-onboarding-modal branch December 18, 2025 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments