Skip to content

New rules layout#713

Merged
elie222 merged 21 commits intomainfrom
feat/new-rules-layout
Aug 25, 2025
Merged

New rules layout#713
elie222 merged 21 commits intomainfrom
feat/new-rules-layout

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Aug 24, 2025

Summary by CodeRabbit

  • New Features

    • New Rules tab with a multi-step, persona-driven "Add new rules" workflow, rich editor, examples panel, processing dialog, and Created Rules modal.
    • AI-assisted "Edit via AI" option that preloads chat and opens the chat sidebar.
  • Improvements

    • Chat sidebar opening preserves other open sidebars (multiple sidebars can remain open).
    • Examples are provider-aware and styling is customizable.
    • Assistant onboarding onComplete is optional; Rule form adds Cancel and UI/labeling refinements.
  • Misc

    • Version bumped to v2.6.0.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 24, 2025

Walkthrough

Provider-aware AI rule schema introduced; prompt-to-rules split into new (create-only) and legacy (old) implementations; new RulesPrompt workflow, RulesTab, and CreatedRulesModal added; Examples and Rules UI updated; createRulesAction/createRulesBody and CreateRuleResult type added; RuleForm/RuleDialog APIs adjusted; tests updated and version bumped to v2.6.0.

Changes

Cohort / File(s) Summary
AI rule generation core
apps/web/utils/ai/rule/prompt-to-rules.ts, apps/web/utils/ai/rule/prompt-to-rules-old.ts, apps/web/utils/ai/rule/create-rule-schema.ts, apps/web/utils/actions/ai-rule.ts, apps/web/utils/actions/rule.validation.ts
Split prompt-to-rules into a create-only implementation and a legacy/old implementation; createRuleSchema changed to createRuleSchema(provider); added createRulesAction and createRulesBody schema; adjusted imports, types, and logging.
New RulesPrompt workflow & Tab
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx, apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx, apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
Added persona-driven rich-editor rules creation UI (RulesPromptNew), CreatedRulesModal/Content for post-create inspection, and a new RulesTab integrating the prompt flow and createRulesAction.
Examples & RulesPrompt integration
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx, apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
Examples now accepts provider and fetches prompts internally; RulesPrompt/RulesPromptForm updated to pass provider, simplified submission flow, and removed examplePrompts prop usage.
Rules list UI & AI edit flow
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
Added "Edit via AI" action (hidden for cold-email-blocker); integrated useSidebar/useChat to preload chat input and open chat sidebar; added showAddRuleButton prop.
Sidebar/chat open behavior
apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx, apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
Use functional updater for setOpen to append "chat-sidebar" (preserve existing open sidebars) instead of replacing the array.
Assistant onboarding & processing dialog
apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx, apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
Made onComplete optional; ProcessingPromptFileDialog final view simplified to render CreatedRulesContent with `result: CreateRuleResult[]
Rule form interaction & deletion
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx, apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx
RuleForm gained optional onCancel prop and Cancel button; added TrashIcon and overlay RemoveButton UI; RuleDialog now passes onCancel={onClose} to RuleForm.
Tests
apps/web/__tests__/ai-prompt-to-rules.test.ts
Test updated to import aiPromptToRulesOld (aliased) and validate generated rules with createRuleSchema(emailAccount.account.provider).parse(rule).
Types & helpers
apps/web/utils/rule/types.ts
Added CreateRuleResult type alias (derived from safeCreateRule return type).
Minor UI tweaks
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx, apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx
Examples scroll area class made configurable via className prop; LearnedPatterns trigger Button variant changed to outline.
Version
version.txt
Bumped release version from v2.5.49 to v2.6.0.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant RulesPrompt as RulesPromptNew (UI)
  participant Editor as SimpleRichTextEditor
  participant Persona as PersonaDialog
  participant Examples as ExamplesList
  participant Server as createRulesAction
  participant Toast as toast.promise

  User->>RulesPrompt: Open Rules tab
  RulesPrompt->>Editor: Initialize editorRef
  User->>Persona: Choose persona
  Persona-->>RulesPrompt: Selected persona
  RulesPrompt->>Editor: Append persona prompt

  User->>Examples: Click example
  Examples-->>RulesPrompt: Example text
  RulesPrompt->>Editor: Append example text

  User->>RulesPrompt: Submit form
  RulesPrompt->>Toast: Show pending
  RulesPrompt->>Server: createRulesAction({ prompt })
  Server-->>RulesPrompt: { rules }
  RulesPrompt->>Toast: Show success
Loading
sequenceDiagram
  autonumber
  actor User
  participant Rules as Rules.tsx
  participant Chat as useChat
  participant Sidebar as useSidebar

  User->>Rules: Open rule action menu
  alt Edit via AI selected
    Rules->>Chat: setInput("Edit rule ‘<name>’ ...")
    Rules->>Sidebar: setOpen(prev => [...prev, "chat-sidebar"])
  else Edit manually selected
    Rules->>Rules: Open manual edit form
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • edulelis

Poem

I thump my paws on meadow keys,
Prompts and rules sprout in spring breeze,
Sidebars open, personas hum,
Editor, AI — hop, go, run!
The rabbit cheers — new rules are done. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2eb20 and a48485a.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/new-rules-layout

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@vercel
Copy link

vercel bot commented Aug 24, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
inbox-zero Ready Ready Preview Aug 25, 2025 1:20pm

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx (1)

1-1: Missing "use client" directive – this component uses hooks and event handlers.

This module calls React hooks (useState, custom hooks) and attaches onClick handlers, so it must be a client component per our project rule for apps/web/app/**/*.tsx. Add the directive to avoid Next.js RSC/runtime issues.

Apply:

+ "use client";
+
 import { MessageCircleIcon } from "lucide-react";
 import { useState } from "react";
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)

1-1: Add "use client" – this component attaches onClick handlers.

Per our guideline for apps/web/app/**/*.tsx, components with onClick must be client components. Add the directive.

+ "use client";
+
 import { memo } from "react";
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)

1-1: Missing "use client" — this component uses React hooks and onClick.

This file renders interactive UI and calls hooks; it must be a Client Component.

Apply at the very top:

+ "use client";
+
 import { useCallback, useEffect, useState } from "react";
🧹 Nitpick comments (20)
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx (1)

77-77: Prevent duplicate sidebar entries when opening the chat.

Appending blindly can introduce repeated "chat-sidebar" keys. Deduplicate in the updater.

-    setOpen((arr) => [...arr, "chat-sidebar"]);
+    setOpen((arr) =>
+      arr.includes("chat-sidebar") ? arr : [...arr, "chat-sidebar"],
+    );
apps/web/utils/actions/rule.validation.ts (1)

176-178: Tighten validation for prompt input and align naming with existing schemas.

  • Consider enforcing a non-empty prompt after trim to prevent accidental empty submissions.
  • Optional: For consistency with saveRulesPromptBody, either document the difference (prompt vs rulesPrompt) or harmonize the field name to reduce mental overhead.
-export const createRulesBody = z.object({ prompt: z.string().trim() });
+export const createRulesBody = z.object({
+  prompt: z
+    .string()
+    .trim()
+    .min(1, "Please enter a prompt"),
+});
 export type CreateRulesBody = z.infer<typeof createRulesBody>;

If you prefer harmonizing the property name:

-export const createRulesBody = z.object({ prompt: z.string().trim() });
+export const createRulesBody = z.object({ rulesPrompt: z.string().trim().min(1, "Please enter a prompt") });

I can update the corresponding action and callers if you want to standardize.

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

11-13: Prop API change looks good; minor DX suggestion.

The provider + className props make the component self-contained and configurable. Consider documenting the expected provider values (e.g., "gmail" | "outlook") via a union type to catch typos at compile time.

-  provider: string;
+  provider: "gmail" | "outlook" | string; // refine if you have a Provider type
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)

141-147: Add a responsive sizes attribute to Next/Image to avoid downloading a 800×600 image on mobile.

   <Image
     src="/images/assistant/rules.png"
     alt="Analyzing prompt file"
     width={800}
     height={600}
+    sizes="(min-width: 768px) 800px, 100vw"
     className="rounded-lg shadow"
   />

231-256: Provide a friendly fallback when no changes were made.

If no rules were created/edited/removed, getDescription() returns an empty string. Consider a default message to avoid an empty description block.

   function getDescription() {
     let message = "";
@@
-    return message;
+    return message || "No changes were needed this time.";
   }
apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx (1)

14-15: Prevent duplicate sidebar entries when clicking multiple times.

-      onClick={() => setOpen((arr) => [...arr, "chat-sidebar"])}
+      onClick={() =>
+        setOpen((arr) =>
+          arr.includes("chat-sidebar") ? arr : [...arr, "chat-sidebar"],
+        )
+      }
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)

350-361: Avoid pushing duplicate "chat-sidebar" entries.

-                              <DropdownMenuItem
-                                onClick={() => {
-                                  setInput(
-                                    `I'd like to edit the "${rule.name}" rule:\n`,
-                                  );
-                                  setOpen((arr) => [...arr, "chat-sidebar"]);
-                                }}
-                              >
+                              <DropdownMenuItem
+                                onClick={() => {
+                                  setInput(
+                                    `I'd like to edit the "${rule.name}" rule:\n`,
+                                  );
+                                  setOpen((arr) =>
+                                    arr.includes("chat-sidebar")
+                                      ? arr
+                                      : [...arr, "chat-sidebar"],
+                                  );
+                                }}
+                              >
apps/web/__tests__/ai-prompt-to-rules.test.ts (2)

32-32: Use console.debug with a descriptive prefix for AI test artifacts.

Project guidelines prefer descriptive debug statements in AI tests. Switch from console.log and add context.

-    console.log(JSON.stringify(result, null, 2));
+    console.debug("[ai-prompt-to-rules] Result:", JSON.stringify(result, null, 2));

2-2: Add tests for the new prompt-to-rules implementation

We currently only exercise the legacy aiPromptToRulesOld in
apps/web/__tests__/ai-prompt-to-rules.test.ts. The newer aiPromptToRules from
@/utils/ai/rule/prompt-to-rules.ts is already wired up in production code
(apps/web/utils/actions/ai-rule.ts:23) but has no dedicated tests. To avoid regressions when switching providers or flows, please add a parallel test suite covering the new implementation.

• Legacy test coverage:
– File: apps/web/__tests__/ai-prompt-to-rules.test.ts
– Imports only aiPromptToRulesOld
• Production usage:
– File: apps/web/utils/actions/ai-rule.ts (line 23)
– Imports aiPromptToRules from @/utils/ai/rule/prompt-to-rules
• Suggested action:
– Create a new test file (e.g. apps/web/__tests__/ai-prompt-to-rules-new.test.ts)
– Mirror the existing test cases against the new function signature and expected outputs

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

36-36: Explicit legacy path import is fine — just keep drift under control

Bringing in aiPromptToRulesOld for the “diff/edit” flows makes sense since it supports ruleId for updates. To avoid long-term drift between old/new convertors, consider extracting the shared system prompt and schema selection into a small utility used by both modules.


345-353: Using aiPromptToRulesOld for added rules in edit flow is appropriate

This ensures the schema can include categories and remains compatible with the edited-rules path. Minor type clarity suggestion: addedRules is typed as Awaited<ReturnType>; since this branch resolves to aiPromptToRulesOld, widening the variable to CreateOrUpdateRuleSchemaWithCategories[] would be clearer for future readers.


471-475: New-prompt branch correctly routes through the old converter for bulk additions

Consistent with the diff/edit path. Same note as above about clarifying addedRules’ type in this scope.

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

139-156: Remove duplicate submission state toggles; simplify toast.promise usage

isSubmitting is set in both onSubmit and inside saveRulesPromise. The inner setIsSubmitting calls are redundant and can lead to flicker. Suggest trimming state changes to the outer scope only.

-  const saveRulesPromise = async (data: SaveRulesPromptBody) => {
-      setIsSubmitting(true);
+  const saveRulesPromise = async (data: SaveRulesPromptBody) => {
       const result = await saveRulesPromptAction(emailAccountId, data);
 
       if (result?.serverError) {
-        setIsSubmitting(false);
         throw new Error(result.serverError);
       }
@@
-      setIsSubmitting(false);
-
       return result;
     };
@@
-    toast.promise(() => saveRulesPromise({ rulesPrompt: markdown }), {
+    toast.promise(saveRulesPromise({ rulesPrompt: markdown }), {
       loading: "Saving rules... This may take a while to process...",
       success: (result) => {
         const {
           createdRules = 0,
           editedRules = 0,
           removedRules = 0,
         } = result?.data || {};
         setResult({ createdRules, editedRules, removedRules });
@@
         return `Rules saved successfully! ${message}`;
       },
       error: (err) => {
         return `Error saving rules: ${err.message}`;
       },
     });

Also, passing the Promise directly to toast.promise avoids an extra wrapper function.

Also applies to: 163-187

apps/web/utils/ai/rule/prompt-to-rules-old.ts (3)

93-99: Fix typo in system prompt

“conver them into rules” → “convert them into rules”. Small, but worth correcting to avoid ambiguity in model instructions.

-  return `You are an AI assistant that converts email management rules into a structured format. Parse the given prompt file and conver them into rules.
+  return `You are an AI assistant that converts email management rules into a structured format. Parse the given prompt file and convert them into rules.

51-76: Add retry around the LLM call and cap prompt length to control token usage

Per the AI utils guidelines, add withRetry for transient failures and truncate overly long inputs to a safe limit before sending to the model. Consider centralizing truncation logic in a helper to keep this module and the new one in sync.

I can wire in withRetry(createGenerateObject(...)) and a shared truncateForModel helper used by both prompt-to-rules modules.


88-221: De-duplicate the system prompt with the new converter to prevent drift

The examples and instruction text are nearly identical to prompt-to-rules.ts but already have small wording differences. Extract getSystemPrompt to a shared utility (e.g., utils/ai/rule/prompts.ts) and pass flags like hasSmartCategories to tailor sections.

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

9-11: Remove unused type import

CreateRulesBody is imported but not used. Drop it to satisfy linters and reduce bundle size.

-import type { CreateRulesBody } from "@/utils/actions/rule.validation";

95-135: Submit flow looks solid; add an empty-prompt guard for UX parity with server

Mirror the server-side guard to short-circuit when the editor is empty and provide immediate feedback:

   const onSubmit = useCallback(async () => {
     const markdown = editorRef.current?.getMarkdown();
-    if (typeof markdown !== "string") return;
+    if (typeof markdown !== "string") return;
+    if (!markdown.trim()) {
+      toast.error("Please write at least one rule or example.");
+      return;
+    }

Also consider moving mutate() and navigation into the success handler only; they are already inside the toast.promise closure, so you’re fine, but keeping all state transitions in one place reduces race conditions.

apps/web/utils/ai/rule/prompt-to-rules.ts (2)

41-46: Minor copy consistency and shared prompt extraction

  • The prompt header still says “prompt file”; elsewhere (system prompt) you call it “prompt”. Consider picking one and using it across both modules. Purely cosmetic.
  • As noted on the legacy module, extract getSystemPrompt to a shared helper to avoid drift.
-  const prompt = `Convert the following prompt file into rules:
+  const prompt = `Convert the following prompt into rules:

Also applies to: 77-205


55-60: Add retry/error classification around the model call

Wrapping generateObject in a retry helper with backoff and classifying provider timeouts/transient errors would make this path more robust. Optional, but recommended per the AI utils guidelines.

I can add withRetry(...) here and wire provider-specific retryable error checks.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a1d4252 and 040409d.

📒 Files selected for processing (15)
  • apps/web/__tests__/ai-prompt-to-rules.test.ts (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (7 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (4 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (4 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (1 hunks)
  • apps/web/utils/actions/ai-rule.ts (7 hunks)
  • apps/web/utils/actions/rule.validation.ts (1 hunks)
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts (1 hunks)
  • apps/web/utils/ai/rule/prompt-to-rules.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (28)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/utils/actions/rule.validation.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/actions/ai-rule.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/__tests__/ai-prompt-to-rules.test.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/actions/*.validation.ts

📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)

Define Zod schemas for validation in dedicated files and use them for both client and server validation.

Define input validation schemas using Zod in the corresponding .validation.ts file. These schemas are used by next-safe-action (.schema()) and can also be reused on the client for form validation.

Files:

  • apps/web/utils/actions/rule.validation.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/{ai,llms}/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*.ts: Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llms
Keep system and user prompts separate; system defines role/task, user contains data/context
Always validate LLM responses with a specific Zod schema
Use descriptive scoped loggers per feature and log inputs/outputs with appropriate levels and context
Implement early returns for invalid inputs and use proper error types with logging
Add fallbacks for AI failures and include retry logic for transient errors using withRetry
Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Use TypeScript types for all parameters/returns and define interfaces for complex IO structures
Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments
Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object
Derive model options using getModel(...) and pass them to createGenerateObject and the generate call

Files:

  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
apps/web/__tests__/**/*

📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)

Place all LLM-related tests in apps/web/tests/

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
apps/web/__tests__/**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)

apps/web/__tests__/**/*.test.ts: Always create helper functions for common test data in LLM-related tests
Include standard test cases: happy path, error handling, edge cases (empty input, null values), different user configurations, and various input formats in LLM-related tests
Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
Use descriptive console.debug statements for generated content in LLM-related tests
Do not mock the LLM call in LLM-related tests; call the actual LLM
Test both AI and non-AI paths in LLM-related tests

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
**/*.test.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

Include security tests in your test suites to verify authentication, authorization, and error handling.

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
**/*.test.{ts,js,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)

**/*.test.{ts,js,tsx,jsx}: Tests are colocated next to the tested file (e.g., dir/format.ts and dir/format.test.ts)
Use vi.mock("server-only", () => ({})); to mock the server-only module in tests
Mock @/utils/prisma in tests using vi.mock("@/utils/prisma") and use the provided prisma mock
Mock external dependencies in tests
Clean up mocks between tests
Do not mock the Logger

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
**/__tests__/**/*.{ts,js,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)

AI tests are placed in the __tests__ directory and are not run by default (they use a real LLM)

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't use export or module.exports in test files.
Don't use focused tests.
Don't use disabled tests.
Make sure the assertion function, like expect, is placed inside an it() function call.
Don't nest describe() blocks too deeply in test files.
Don't use focused tests.
Don't use disabled tests.
Don't use export or module.exports in test files.

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
apps/web/__tests__/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)

Place LLM-specific tests under apps/web/tests

Files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
🧠 Learnings (14)
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod schemas for validation in dedicated files and use them for both client and server validation.

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define input validation schemas using Zod in the corresponding `.validation.ts` file. These schemas are used by `next-safe-action` (`.schema()`) and can also be reused on the client for form validation.

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use Zod schemas for validation on both client and server

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
📚 Learning: 2025-07-18T15:04:57.115Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.115Z
Learning: Applies to **/*.ts : Define validation schemas using Zod

Applied to files:

  • apps/web/utils/actions/rule.validation.ts
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system and user prompts separate; system defines role/task, user contains data/context

Applied to files:

  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/utils/actions/ai-rule.ts
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent

Applied to files:

  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/ai/rule/prompt-to-rules.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments

Applied to files:

  • apps/web/utils/ai/rule/prompt-to-rules-old.ts
  • apps/web/utils/actions/ai-rule.ts
📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Test both AI and non-AI paths in LLM-related tests

Applied to files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
📚 Learning: 2025-06-23T12:27:30.570Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.

Applied to files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
📚 Learning: 2025-06-23T12:26:53.882Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.

Applied to files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Make sure the assertion function, like expect, is placed inside an it() function call.

Applied to files:

  • apps/web/__tests__/ai-prompt-to-rules.test.ts
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Derive model options using getModel(...) and pass them to createGenerateObject and the generate call

Applied to files:

  • apps/web/utils/ai/rule/prompt-to-rules.ts
🧬 Code graph analysis (11)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
apps/web/components/Button.tsx (1)
  • Button (60-87)
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (3)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1)
  • RulesPrompt (31-63)
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
  • Rules (66-493)
apps/web/app/(app)/[emailAccountId]/assistant/RulesTab.tsx (1)
  • RulesTab (5-24)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useModal.tsx (1)
  • useModal (3-9)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
  • getPersonas (85-348)
apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
  • PersonaDialog (7-40)
apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (1)
  • AssistantOnboarding (17-77)
apps/web/hooks/useRules.tsx (1)
  • useRules (4-8)
apps/web/hooks/useLabels.ts (1)
  • useLabels (62-88)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/components/editor/SimpleRichTextEditor.tsx (2)
  • SimpleRichTextEditorRef (22-25)
  • SimpleRichTextEditor (27-153)
apps/web/utils/actions/ai-rule.ts (1)
  • createRulesAction (515-578)
apps/web/utils/path.ts (1)
  • prefixPath (1-4)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
  • ProcessingPromptFileDialog (35-83)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
  • RuleDialog (23-79)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
  • getExamplePrompts (70-72)
apps/web/utils/ai/rule/prompt-to-rules-old.ts (6)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/ai/rule/create-rule-schema.ts (3)
  • createRuleSchema (100-107)
  • CreateOrUpdateRuleSchemaWithCategories (147-150)
  • getCreateRuleSchemaWithCategories (109-136)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-29)
apps/web/utils/mention.ts (1)
  • convertMentionsToLabels (6-8)
apps/web/utils/llms/model.ts (1)
  • getModel (27-41)
apps/web/utils/llms/index.ts (1)
  • createGenerateObject (118-166)
apps/web/__tests__/ai-prompt-to-rules.test.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
  • createRuleSchema (100-107)
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (3)
apps/web/hooks/useRules.tsx (1)
  • useRules (4-8)
apps/web/components/ui/sidebar.tsx (1)
  • useSidebar (821-821)
apps/web/providers/ChatProvider.tsx (1)
  • useChat (116-122)
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (3)
  • CreateRuleSchemaWithCategories (139-146)
  • getCreateRuleSchemaWithCategories (109-136)
  • createRuleSchema (100-107)
apps/web/utils/actions/ai-rule.ts (6)
apps/web/utils/ai/rule/prompt-to-rules-old.ts (1)
  • aiPromptToRulesOld (20-86)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (46-86)
apps/web/utils/actions/rule.validation.ts (1)
  • createRulesBody (176-176)
apps/web/utils/error.ts (1)
  • SafeError (86-96)
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
  • aiPromptToRules (15-70)
apps/web/utils/rule/rule.ts (1)
  • safeCreateRule (34-100)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
  • Examples (53-53)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
apps/web/utils/string.ts (1)
  • pluralize (40-46)
🔇 Additional comments (22)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

954-961: LGTM: remove redundant disabled prop (Button already disables when loading).

Consistent with Button component behavior (disabled={loading || props.disabled}). Keeps UX identical while simplifying props.

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

20-20: onComplete prop can safely remain optional
A search for any <AssistantOnboarding usages across apps/web returned no call sites supplying—or depending on—a non-optional onComplete callback. Since no consumers expect a mandatory callback, making it optional is safe and ergonomic.

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

18-19: LGTM: derive prompts internally based on provider.

Localizing data derivation reduces prop surface and keeps Examples aligned with provider terminology.


24-24: LGTM: expose scroll container size via className.

Good flexibility while preserving previous defaults.

apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (4)

64-64: DialogContent width tweak looks good.

Using max-w-xl improves readability on small/medium screens without affecting larger viewports.


101-106: Header/layout adjustments improve readability.

mx-auto with left-aligned description is a sensible refinement for content-heavy steps.


241-253: Nice defensive guards for optional counts.

The editedRules/removedRules checks prevent undefined from leaking into the UI.


271-275: Copy change LGTM.

“Try it out!” is concise and consistent with the rest of the flow.

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

4-13: LGTM — clear composition of prompt + rules list, hides Add button in this context.

This keeps the “create via prompt” flow prominent while still surfacing the rules table. No client hooks/onClick here, so leaving it as a Server Component is fine.

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

61-63: Imports for chat/sidebar look correct and localized to this component.


66-72: Public API extension (showAddRuleButton) is clean and backward compatible.

Default remains true; callers can opt-out where needed.


230-235: Conditional rendering of Add Rule button is straightforward and readable.

apps/web/__tests__/ai-prompt-to-rules.test.ts (1)

103-106: Provider-aware validation is a solid improvement.

Parsing with createRuleSchema(emailAccount.account.provider) ensures action validation respects provider capabilities.

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

19-22: Good: centralizing schemas under rule.validation

Switching to import createRulesBody/saveRulesPromptBody from rule.validation aligns server actions with a single validation source. Keeps client/server schemas consistent.


426-435: Edit-flow conversion uses the legacy path correctly

Passing isEditing: true guarantees the presence of optional ruleId for targeted updates. Good call on threading availableCategories to improve structured outputs.


482-493: Concise creation logging

Logging ruleName only reduces noise while keeping traceability. Looks good.

apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (3)

69-69: Passing provider down is the right move

Propagating provider into RulesPromptForm enables provider-aware examples and personas.


97-107: Prop type addition looks good

Adding provider: string to RulesPromptForm props keeps types explicit and readable.


346-347: Examples now provider-driven — good

Switching Examples to resolve its own prompts via provider removes coupling to RulesPrompt and keeps that logic localized.

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

146-155: Processing dialog result shape — verify compatibility

ProcessingPromptFileDialog previously displayed created/edited/removed counts. Here you pass only { createdRules }. Ensure the dialog’s ResultProps accepts this shape (or handle missing edited/removed gracefully). If not, update the dialog’s types to a union.


263-271: Examples panel integration looks good

Provider-based Examples with a bounded height keeps the layout neat. Toggling via isExamplesOpen avoids unnecessary editor re-renders.

apps/web/utils/ai/rule/prompt-to-rules.ts (1)

23-33: Schema selection logic is clean and provider-aware

Good use of getCreateRuleSchemaWithCategories when categories are available; otherwise falling back to createRuleSchema(provider).

Comment on lines 515 to 579
export const createRulesAction = actionClient
.metadata({ name: "createRules" })
.schema(createRulesBody)
.action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => {
const emailAccount = await prisma.emailAccount.findUnique({
where: { id: emailAccountId },
select: {
id: true,
email: true,
userId: true,
about: true,
rulesPrompt: true,
categories: { select: { id: true, name: true } },
user: {
select: {
aiProvider: true,
aiModel: true,
aiApiKey: true,
},
},
account: {
select: {
provider: true,
},
},
},
});

if (!emailAccount) {
logger.error("Email account not found");
throw new SafeError("Email account not found");
}

const addedRules = await aiPromptToRules({
emailAccount,
promptFile: prompt,
availableCategories: emailAccount.categories.map((c) => c.name),
});

logger.info("Rules to be added", {
emailAccountId,
count: addedRules?.length || 0,
});

// add new rules
for (const rule of addedRules || []) {
logger.info("Creating rule", { emailAccountId, ruleName: rule.name });

await safeCreateRule({
result: rule,
emailAccountId,
categoryNames: rule.condition.categories?.categoryFilters || [],
shouldCreateIfDuplicate: false,
provider: emailAccount.account.provider,
});
}

logger.info("Completed", {
emailAccountId,
createdRules: addedRules?.length || 0,
});

return { createdRules: addedRules?.length || 0 };
});

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Harden createRulesAction: validate input, handle AI failures, and revalidate cache

The action works, but a few upgrades will improve resilience and UX:

  • Validate non-empty prompt on the server (z.string().min(1)) and/or guard in the action.
  • Wrap the AI call in try/catch and surface SafeError for expected failures.
  • Revalidate any paths/tags that list rules to keep SSR/ISR views in sync.

Proposed patch:

 export const createRulesAction = actionClient
   .metadata({ name: "createRules" })
   .schema(createRulesBody)
   .action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => {
+    if (!prompt?.trim()) {
+      // Defensive guard until schema is tightened to .min(1)
+      throw new SafeError("Prompt is empty");
+    }
     const emailAccount = await prisma.emailAccount.findUnique({
@@
-    const addedRules = await aiPromptToRules({
-      emailAccount,
-      promptFile: prompt,
-      availableCategories: emailAccount.categories.map((c) => c.name),
-    });
+    let addedRules;
+    try {
+      addedRules = await aiPromptToRules({
+        emailAccount,
+        promptFile: prompt,
+        availableCategories: emailAccount.categories.map((c) => c.name),
+      });
+    } catch (err) {
+      logger.error("AI error converting prompt to rules", {
+        emailAccountId,
+        error: err instanceof Error ? { message: err.message, name: err.name } : err,
+      });
+      throw new SafeError("Could not convert prompt to rules. Please try again.");
+    }
@@
-    logger.info("Completed", {
+    logger.info("Completed", {
       emailAccountId,
       createdRules: addedRules?.length || 0,
     });
 
-    return { createdRules: addedRules?.length || 0 };
+    // Optional: keep pages/lists fresh if you rely on ISR/SSR caches.
+    // revalidatePath(`/[emailAccountId]/automation?tab=rules`);
+    return { createdRules: addedRules?.length || 0 };
   });

If you are using route tags for rules listing, prefer revalidateTag("rules:") over revalidatePath.

Would you like me to also tighten createRulesBody to z.string().trim().min(1, "Prompt is empty") in rule.validation.ts?


🏁 Script executed:

#!/bin/bash
set -e
# Locate createRulesBody definition
rg -n "createRulesBody" -C3
# Locate aiPromptToRules definition
rg -n "aiPromptToRules" -C3

Length of output: 10669


🏁 Script executed:

#!/bin/bash
set -e
# Search for any existing revalidation calls
rg -n "revalidatePath" -C3
rg -n "revalidateTag" -C3

Length of output: 10969


Harden createRulesAction: validate prompt, handle AI errors, and revalidate cache

Please address the following mandatory refactors to improve resilience, UX, and cache consistency:

  • Enforce non-empty prompts both at the schema level and in-action
    • In apps/web/utils/actions/rule.validation.ts, change

    export const createRulesBody = z.object({ prompt: z.string().trim() });

    to

    export const createRulesBody = z.object({
      prompt: z.string().trim().min(1, "Prompt is empty"),
    });

    • In createRulesAction (apps/web/utils/actions/ai-rule.ts), add at the top of the action:

    if (!prompt?.trim()) {
      throw new SafeError("Prompt is empty");
    }
  • Wrap the AI invocation in try/catch and convert failures into user-friendly SafeErrors

      let addedRules;
      try {
        addedRules = await aiPromptToRules({ emailAccount, promptFile: prompt, availableCategories });
      } catch (err) {
        logger.error("AI error converting prompt to rules", {
          emailAccountId,
          error: err instanceof Error ? { name: err.name, message: err.message } : err,
        });
        throw new SafeError("Could not convert prompt to rules. Please try again.");
      }
  • Invalidate your ISR/SSR cache so new rules appear immediately
    • Import at the top of ai-rule.ts:

    import { revalidatePath } from "next/cache";
    import { prefixPath } from "@/utils/actions/helpers"; // or wherever this helper lives

    • After creating rules, call:

    revalidatePath(prefixPath(emailAccountId, "/automation?tab=rules"));

These changes align with existing patterns for server-action validation, error handling, and cache invalidation across the codebase.

🤖 Prompt for AI Agents
In apps/web/utils/actions/ai-rule.ts around lines 515 to 579, the action does
not validate empty prompts, does not handle AI invocation errors, and does not
revalidate the ISR/SSR cache after creating rules; update rule.validation.ts to
make prompt required (trim().min(1,"Prompt is empty")), add a top-of-action
guard that throws SafeError when prompt is empty/blank, wrap the
aiPromptToRules(...) call in try/catch and convert any thrown error into a
user-friendly SafeError, import revalidatePath and prefixPath at the top of
ai-rule.ts, and after the loop that creates rules call
revalidatePath(prefixPath(emailAccountId, "/automation?tab=rules")) so new rules
are visible immediately.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

516-586: Harden createRulesAction: validate prompt, handle AI errors, and revalidate cache

These were previously requested and still apply. Please:

  • Enforce non-empty prompt.
  • Wrap aiPromptToRules in try/catch and convert failures into a user-facing SafeError.
  • Revalidate relevant paths/tags so UI reflects new rules immediately.
 export const createRulesAction = actionClient
   .metadata({ name: "createRules" })
   .schema(createRulesBody)
   .action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => {
+    if (!prompt?.trim()) {
+      throw new SafeError("Prompt is empty");
+    }
     const emailAccount = await prisma.emailAccount.findUnique({
@@
-    const addedRules = await aiPromptToRules({
-      emailAccount,
-      promptFile: prompt,
-      availableCategories: emailAccount.categories.map((c) => c.name),
-    });
+    let addedRules;
+    try {
+      addedRules = await aiPromptToRules({
+        emailAccount,
+        promptFile: prompt,
+        availableCategories: emailAccount.categories.map((c) => c.name),
+      });
+    } catch (err) {
+      logger.error("AI error converting prompt to rules", {
+        emailAccountId,
+        error:
+          err instanceof Error
+            ? { name: err.name, message: err.message }
+            : err,
+      });
+      throw new SafeError("Could not convert prompt to rules. Please try again.");
+    }
@@
-    return {
-      rules: createdRules,
-    };
+    // Optional: keep any ISR/SSR views in sync if applicable
+    // import { revalidatePath } from "next/cache";
+    // import { prefixPath } from "@/utils/path";
+    // revalidatePath(prefixPath(emailAccountId, "/automation"));
+    return { rules: createdRules };
   });

Also tighten the schema in apps/web/utils/actions/rule.validation.ts:

-export const createRulesBody = z.object({ prompt: z.string().trim() });
+export const createRulesBody = z.object({
+  prompt: z.string().trim().min(1, "Prompt is empty"),
+});
🧹 Nitpick comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (3)

94-101: Guard against empty prompts on the client

Early-return if the editor contains only whitespace. This avoids a server round-trip and aligns with server-side validation you’ll add.

   const onSubmit = useCallback(async () => {
-    const markdown = editorRef.current?.getMarkdown();
-    if (typeof markdown !== "string") return;
+    const markdown = editorRef.current?.getMarkdown();
+    if (typeof markdown !== "string") return;
+    if (!markdown.trim()) {
+      toast.error("Please write a prompt before creating rules.");
+      return;
+    }

59-61: Restore onboarding → persona handoff

In the old flow, onboarding completed by auto-opening the persona picker when no prompt existed. Consider parity here for a smoother first-run:

-  <AssistantOnboarding />
+  <AssistantOnboarding
+    onComplete={() => {
+      onOpenPersonaDialog();
+    }}
+  />

This keeps the progressive guidance intact.

Also applies to: 33-41


179-185: Nit: avoid passing undefined as defaultValue

The editor treats defaultValue as optional. Passing undefined is redundant.

-  <SimpleRichTextEditor
-    ref={editorRef}
-    defaultValue={undefined}
+  <SimpleRichTextEditor
+    ref={editorRef}
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)

47-50: Decorative SVG should be hidden from screen readers

Add aria-hidden for decorative icons or provide an accessible title. Since the heading already conveys success, hide the icon:

-  <CheckCircle2 className="size-5 text-green-600" />
+  <CheckCircle2 aria-hidden className="size-5 text-green-600" />
apps/web/utils/actions/ai-rule.ts (1)

520-542: Avoid duplicating selection logic: reuse getEmailAccountWithAi

You already have getEmailAccountWithAi({ emailAccountId }) above. Reuse it for consistency and to centralize selection changes.

-    const emailAccount = await prisma.emailAccount.findUnique({
-      where: { id: emailAccountId },
-      select: {
-        id: true,
-        email: true,
-        userId: true,
-        about: true,
-        rulesPrompt: true,
-        categories: { select: { id: true, name: true } },
-        user: {
-          select: {
-            aiProvider: true,
-            aiModel: true,
-            aiApiKey: true,
-          },
-        },
-        account: {
-          select: {
-            provider: true,
-          },
-        },
-      },
-    });
+    const emailAccount = await getEmailAccountWithAi({ emailAccountId });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 040409d and 0c9fd1a.

📒 Files selected for processing (4)
  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1 hunks)
  • apps/web/utils/actions/ai-rule.ts (7 hunks)
  • apps/web/utils/rule/types.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/utils/rule/types.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/utils/actions/ai-rule.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/utils/rule/types.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/utils/actions/ai-rule.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/utils/rule/types.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/utils/actions/ai-rule.ts
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/utils/rule/types.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/utils/actions/ai-rule.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/utils/rule/types.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
  • apps/web/utils/actions/ai-rule.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/rule/types.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/rule/types.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/rule/types.ts
  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/ai-rule.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/ai-rule.ts
🧠 Learnings (3)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments

Applied to files:

  • apps/web/utils/actions/ai-rule.ts
📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the `next-safe-action` library for type safety, input validation, context management, and error handling. Refer to `apps/web/utils/actions/safe-action.ts` for client definitions (`actionClient`, `actionClientUser`, `adminActionClient`).

Applied to files:

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

Applied to files:

  • apps/web/utils/actions/ai-rule.ts
🧬 Code graph analysis (4)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (7)
apps/web/utils/rule/types.ts (1)
  • CreateRuleResult (3-5)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/utils/path.ts (1)
  • prefixPath (1-4)
apps/web/utils/condition.ts (1)
  • conditionsToString (194-227)
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
  • ActionBadges (495-525)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
  • RuleDialog (23-79)
apps/web/utils/rule/types.ts (1)
apps/web/utils/rule/rule.ts (1)
  • safeCreateRule (34-100)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
  • RulesPrompt (39-93)
  • RulesPromptForm (99-367)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useModal.tsx (1)
  • useModal (3-9)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
  • getPersonas (85-348)
apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
  • PersonaDialog (7-40)
apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (1)
  • AssistantOnboarding (17-77)
apps/web/hooks/useRules.tsx (1)
  • useRules (4-8)
apps/web/hooks/useLabels.ts (1)
  • useLabels (62-88)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/components/editor/SimpleRichTextEditor.tsx (2)
  • SimpleRichTextEditorRef (22-25)
  • SimpleRichTextEditor (27-153)
apps/web/utils/actions/ai-rule.ts (1)
  • createRulesAction (516-586)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
  • ProcessingPromptFileDialog (35-83)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
  • CreatedRulesModal (24-109)
apps/web/utils/actions/ai-rule.ts (7)
apps/web/utils/ai/rule/prompt-to-rules-old.ts (1)
  • aiPromptToRulesOld (20-86)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (46-86)
apps/web/utils/actions/rule.validation.ts (1)
  • createRulesBody (176-176)
apps/web/utils/error.ts (1)
  • SafeError (86-96)
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
  • aiPromptToRules (15-70)
apps/web/utils/rule/types.ts (1)
  • CreateRuleResult (3-5)
apps/web/utils/rule/rule.ts (1)
  • safeCreateRule (34-100)
🔇 Additional comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)

74-77: Type safety check: conditionsToString expects RuleConditions

Confirm that CreateRuleResult includes the properties conditionalOperator, instructions, categoryFilterType, and categoryFilters at the top level. If not, pass the correct shape or map fields before calling conditionsToString(rule).

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

1-5: LGTM: Stable type alias and import hygiene

  • import type prevents runtime dependency.
  • NonNullable<Awaited<ReturnType<...>>> matches how you filter falsy results before pushing.

No changes needed.

Comment on lines 60 to 66
{rules.map((rule) => (
<Card
key={rule.id}
className="p-4 hover:shadow-md transition-shadow cursor-pointer"
onClick={() => ruleDialog.open({ ruleId: rule.id })}
>
<div className="space-y-2">
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

Make the clickable Card keyboard-accessible

Cards are clickable divs with onClick only. Per guidelines, accompany onClick with keyboard handlers and ensure focusability/role.

-  <Card
+  <Card
     key={rule.id}
-    className="p-4 hover:shadow-md transition-shadow cursor-pointer"
-    onClick={() => ruleDialog.open({ ruleId: rule.id })}
+    className="p-4 hover:shadow-md transition-shadow cursor-pointer"
+    role="button"
+    tabIndex={0}
+    onClick={() => ruleDialog.open({ ruleId: rule.id })}
+    onKeyDown={(e) => {
+      if (e.key === "Enter" || e.key === " ") {
+        e.preventDefault();
+        ruleDialog.open({ ruleId: rule.id });
+      }
+    }}
   >

Also applies to: 92-99

🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx around
lines 60-66 (and similarly 92-99), the Card elements are only clickable via
onClick which is not keyboard-accessible; make them focusable and handle
keyboard activation by adding tabIndex={0}, role="button" (or use a native
button element), and an onKeyDown handler that triggers the same action when
Enter or Space is pressed; ensure you also preserve the onClick handler and
include an accessible label/aria-label if the card content is not descriptive
enough.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1)

78-87: Fix: Created rules modal never opens and Processing dialog never reaches “final” state

  • result never gets populated with counts, so ProcessingPromptFileDialog remains stuck on its waiting step.
  • showCreatedRulesModal is never set to true, so the CreatedRulesModal never opens.
  • This matches an earlier review; re-raising with a concrete patch for this file.

Apply this diff to:

  • Type result to include all counts.
  • Populate result on success.
  • Auto-open CreatedRulesModal either immediately (if the processing walkthrough was already viewed) or right after the dialog is closed on first-time flow.
@@
-  const [isDialogOpen, setIsDialogOpen] = useState(false);
-  const [result, setResult] = useState<{ createdRules: number }>();
+  const [isDialogOpen, setIsDialogOpen] = useState(false);
+  const [result, setResult] = useState<{
+    createdRules: number;
+    editedRules: number;
+    removedRules: number;
+  }>();
   const [createdRules, setCreatedRules] = useState<CreateRuleResult[]>([]);
   const [showCreatedRulesModal, setShowCreatedRulesModal] = useState(false);
@@
       {
         loading: "Creating rules...",
         success: (result) => {
           const { rules = [] } = result?.data || {};
           setCreatedRules(rules);
+          setResult({
+            createdRules: rules.length,
+            editedRules: 0,
+            removedRules: 0,
+          });
+          // If the user already viewed the processing dialog, show the modal immediately.
+          if (viewedProcessingPromptFileDialog) {
+            setShowCreatedRulesModal(true);
+          }
 
           return `${rules.length} rules created!`;
         },
@@
       />
+
+      {/*
+        After first-time users complete the processing dialog (it closes),
+        automatically open the CreatedRulesModal once.
+      */}
+      { /* effect intentionally placed near dialog wiring for locality */ }
+      {(() => {
+        // Inline effect pattern to keep the diff localized to this file section.
+        // If your lint rules disallow IIFEs for effects, move this to the hooks block above.
+        // eslint-disable-next-line react-hooks/rules-of-hooks
+        useEffect(() => {
+          if (!isDialogOpen && createdRules.length > 0 && !showCreatedRulesModal) {
+            setShowCreatedRulesModal(true);
+          }
+        }, [isDialogOpen, createdRules.length, showCreatedRulesModal]);
+        return null;
+      })()}

Also applies to: 142-151, 281-286

🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)

75-77: Pass error to LoadingContent for consistent error handling

Guidelines request using LoadingContent with loading, error, and children. You already provide a skeleton for loading; pass through the error from useLabels() as well.

-  const { userLabels, isLoading: isLoadingLabels } = useLabels();
+  const {
+    userLabels,
+    isLoading: isLoadingLabels,
+    error: labelsError,
+  } = useLabels();
@@
-            <LoadingContent
-              loading={isLoadingLabels}
-              loadingComponent={<Skeleton className="min-h-[70px] w/full" />}
-            >
+            <LoadingContent
+              loading={isLoadingLabels}
+              error={labelsError}
+              loadingComponent={<Skeleton className="min-h-[70px] w-full" />}
+            >

Also applies to: 175-178


30-62: Export name collides with existing RulesPrompt component

This file exports RulesPrompt, and there is already a RulesPrompt in apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx. Even as named exports, this can cause confusion and import mistakes.

  • Ensure all import sites reference the intended module.
  • Optionally rename this export to RulesPromptNew to match the filename.
-export function RulesPrompt() {
+export function RulesPromptNew() {

If you take this route, update the import where this is consumed.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c9fd1a and f999d5d.

📒 Files selected for processing (2)
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1 hunks)
  • version.txt (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • version.txt
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
  • RulesPrompt (39-93)
  • RulesPromptForm (99-367)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useModal.tsx (1)
  • useModal (3-9)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
  • getPersonas (85-348)
apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
  • PersonaDialog (7-40)
apps/web/hooks/useRules.tsx (1)
  • useRules (4-8)
apps/web/hooks/useLabels.ts (1)
  • useLabels (62-88)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/components/editor/SimpleRichTextEditor.tsx (2)
  • SimpleRichTextEditorRef (22-25)
  • SimpleRichTextEditor (27-153)
apps/web/utils/actions/ai-rule.ts (1)
  • createRulesAction (516-586)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
  • ProcessingPromptFileDialog (35-83)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
  • Examples (53-53)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
  • CreatedRulesModal (24-109)
🔇 Additional comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)

84-86: Verify the localStorage key name is intentional

You use the key "viewedProcessingPromptFileDialog3", while the existing flow elsewhere uses "viewedProcessingPromptFileDialog". If unintentional, users who already completed onboarding will see the walkthrough again.

If you meant to reuse the existing flag, change it to:

-  ] = useLocalStorage("viewedProcessingPromptFileDialog3", false);
+  ] = useLocalStorage("viewedProcessingPromptFileDialog", false);

142-151: LGTM: Processing dialog wiring and persona append

Dialog visibility/init flow and the persona-append effect are wired cleanly. Once result is set (see earlier comment), the final step will render correctly.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

236-239: Remove console.error in UI code; rely on toasts and scoped logging policies

The coding guidelines forbid console usage. These console.error calls are redundant given toastError and don’t add value in production.

-        if (res?.serverError) {
-          console.error(res);
+        if (res?.serverError) {
           toastError({ description: res.serverError });
           if (mutate) mutate();
         } else if (!res?.data?.rule) {
-        if (res?.serverError) {
-          console.error(res);
+        if (res?.serverError) {
           toastError({ description: res.serverError });
         } else if (!res?.data?.rule) {

If you need diagnostics during development, consider temporary scoped logging with whatever client-side logging facility you use, but keep it out of production builds.

Also applies to: 265-266

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

925-959: Delete button should track its own loading; don’t couple to form submit state

Using loading={isSubmitting} ties deletion UX to form submission and doesn’t prevent double-deletes. Track deletion state locally, disable while in-flight, and optionally close the dialog after success.

-          {rule.id && (
-            <Button
-              size="sm"
-              variant="outline"
-              loading={isSubmitting}
-              Icon={TrashIcon}
-              onClick={async () => {
+          {rule.id && (
+            <Button
+              size="sm"
+              variant="outline"
+              loading={isDeleting}
+              disabled={isDeleting}
+              Icon={TrashIcon}
+              onClick={async () => {
+                if (isDeleting) return;
+                setIsDeleting(true);
                 const yes = confirm(
                   "Are you sure you want to delete this rule?",
                 );
                 if (yes) {
                   try {
                     const result = await deleteRuleAction(emailAccountId, {
                       id: rule.id!,
                     });
                     if (result?.serverError) {
                       toastError({
                         description: result.serverError,
                       });
                     } else {
                       toastSuccess({
                         description: "The rule has been deleted.",
                       });
                       router.push(
                         prefixPath(emailAccountId, "/automation?tab=rules"),
                       );
+                      // If embedded in a dialog with a cancel/close control, consider closing:
+                      // onCancel?.();
                     }
                   } catch {
                     toastError({ description: "Failed to delete rule." });
+                  } finally {
+                    setIsDeleting(false);
                   }
                 }
               }}
             >
               Delete
             </Button>
           )}

Add state near the other local UI state:

const [isDeleting, setIsDeleting] = useState(false);

Note: Because Button disables when loading, this also prevents accidental double clicks.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f999d5d and 2abfe4f.

📒 Files selected for processing (3)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (4 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
🧠 Learnings (2)
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx
📚 Learning: 2025-07-19T17:50:22.078Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use the `LoadingContent` component to handle loading states

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (4)
apps/web/utils/actions/rule.validation.ts (1)
  • CreateRuleBody (158-158)
apps/web/components/TooltipExplanation.tsx (1)
  • TooltipExplanation (33-60)
apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx (1)
  • LearnedPatternsDialog (20-89)
apps/web/components/Button.tsx (1)
  • Button (60-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Secret Detection
🔇 Additional comments (10)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (4)

56-57: Wire up cancel-to-close (edit mode) — looks good

Passing onCancel={onClose} ensures the form’s Cancel button closes the dialog while editing. Matches the new RuleForm API.


74-75: Wire up cancel-to-close (create mode) — looks good

Same as above, for the create path. Good consistency across both branches.


31-31: No unnecessary fetch—no changes needed

The useRule hook internally passes null to useSWR whenever its ruleId parameter is falsy (including an empty string), which disables any request. Calling useRule(ruleId || "") therefore correctly skips fetching in “create” mode. No further guard or enabled flag is required here.


39-39: Fix Dialog onOpenChange signature and behavior

It appears that the Dialog component’s onOpenChange prop is typed as (open: boolean) => void. Passing your onClose handler (a () => void) directly can lead to an incorrect type and will fire on both open and close transitions. To ensure it only closes when the dialog is dismissed, wrap the call and check the boolean:

-    <Dialog open={isOpen} onOpenChange={onClose}>
+    <Dialog
+      open={isOpen}
+      onOpenChange={(open) => {
+        if (!open) onClose();
+      }}
+    >

Please verify that in your @radix-ui/react-dialog typings, onOpenChange is indeed declared as (open: boolean) => void before merging.

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

24-25: Icon import for delete action — OK

TrashIcon import added and used in the Delete button; keeps iconography consistent with the rest of the UI.


121-135: New onCancel prop for RuleForm — API shape looks good

Optional onCancel?: () => void is clean and only renders the Cancel control when provided. This keeps RuleForm reusable in and out of dialogs.


882-900: Settings > Automate toggle — UX and state wiring look good

  • Uses Toggle with labelRight and TooltipExplanation.
  • Writes back via setValue without over-validating.
    No issues spotted.

902-914: Settings > Apply to threads — good integration

The runOnThreads toggle plus ThreadsExplanation provides clear behavior. State is correctly synced with setValue.


962-968: Cancel button — correct conditional render and default type

Renders only when onCancel exists and, via the Button component, defaults to type="button" so it won’t submit the form.


970-977: Submit buttons now rely on loading instead of disabled — good

Button already disables when loading is true. This improves UX consistency and reduces boilerplate.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

921-951: Prevent accidental form submissions; separate delete loading state

  • The Delete and Cancel buttons do not specify type. Inside a <form>, a button without type defaults to "submit", which risks accidental submissions.
  • The Delete button ties its loading to isSubmitting (form submit), not the delete action. Use a dedicated isDeleting state so the loading spinner reflects delete progress.

Apply these diffs:

-            <Button
-              size="sm"
-              variant="outline"
-              loading={isSubmitting}
-              Icon={TrashIcon}
-              onClick={async () => {
+            <Button
+              size="sm"
+              variant="outline"
+              type="button"
+              loading={isDeleting}
+              Icon={TrashIcon}
+              onClick={async () => {
+                if (isDeleting) return;
                 const yes = confirm(
                   "Are you sure you want to delete this rule?",
                 );
                 if (yes) {
-                  try {
+                  try {
+                    setIsDeleting(true);
                     const result = await deleteRuleAction(emailAccountId, {
                       id: rule.id!,
                     });
                     if (result?.serverError) {
                       toastError({
                         description: result.serverError,
                       });
                     } else {
                       toastSuccess({
                         description: "The rule has been deleted.",
                       });
                       router.push(
                         prefixPath(emailAccountId, "/automation?tab=rules"),
                       );
                     }
-                  } catch {
+                  } catch {
                     toastError({ description: "Failed to delete rule." });
+                  } finally {
+                    setIsDeleting(false);
                   }
                 }
               }}
             >
               Delete
             </Button>
-            <Button variant="outline" onClick={onCancel}>
+            <Button variant="outline" type="button" onClick={onCancel}>
               Cancel
             </Button>

Add the missing local state (outside the selected range; place near other local useState declarations in RuleForm):

const [isDeleting, setIsDeleting] = useState(false);

Also applies to: 958-963

♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)

127-135: Type mismatch: rule.groupId is used but not declared in RuleForm props

rule.groupId is referenced when rendering LearnedPatternsDialog (Line 914) but the rule prop type is CreateRuleBody & { id?: string }. Under strict typing this will error and can break compilation.

Apply this minimal typing fix:

-}: {
-  rule: CreateRuleBody & { id?: string };
+}: {
+  rule: CreateRuleBody & { id?: string; groupId?: string | null };

Optionally, introduce a named alias for clarity:

type RuleFormRule = CreateRuleBody & { id?: string; groupId?: string | null };
// ...
// in the props:
rule: RuleFormRule;

Also applies to: 910-917

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

1242-1264: Preserve user-entered label when toggling “AI generated”.

Current logic resets the label value to an empty string on each toggle, which discards user input. Preserve the existing value and only flip the ai flag.

Apply this diff:

-                    <Toggle
+                    <Toggle
                       name={`actions.${index}.${field.name}.ai`}
                       labelRight="AI generated"
                       enabled={isAiGenerated || false}
-                      onChange={(enabled: boolean) => {
-                        setValue(
-                          `actions.${index}.${field.name}`,
-                          enabled
-                            ? { value: "", ai: true }
-                            : { value: "", ai: false },
-                        );
-                      }}
+                      onChange={(enabled: boolean) => {
+                        const current =
+                          watch(`actions.${index}.${field.name}.value`) || "";
+                        setValue(`actions.${index}.${field.name}`, {
+                          value: current,
+                          ai: enabled,
+                        });
+                      }}
                     />
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2abfe4f and 41f55ec.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (10 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (4)
apps/web/utils/actions/rule.validation.ts (1)
  • CreateRuleBody (158-158)
apps/web/components/TooltipExplanation.tsx (1)
  • TooltipExplanation (33-60)
apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx (1)
  • LearnedPatternsDialog (20-89)
apps/web/components/Button.tsx (1)
  • Button (60-87)
🔇 Additional comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (5)

24-25: Icon import looks correct and consistent.

TrashIcon from lucide-react is used in both the Delete button and the overlay remove buttons. All good.


492-497: Overlay remove control for conditions: UX improvement; confirm CardBasic forwards className.

Nice addition moving remove into an overlay; cleaner and consistent. Please confirm CardBasic forwards className to the container so relative positioning is honored; otherwise the overlay button will misplace.


877-918: Settings section reflow is a net win for clarity.

Grouping automation toggles and learned patterns under “Settings” improves discoverability. The tooltips are tight and informative. No issues.


1085-1086: Action overlay remove control: consistent with Conditions

Good consistency with the Conditions overlay. The absolute-positioned control works as expected when CardBasic is relative.


1450-1451: ThreadsExplanation tooltip side switch

Switching to side="right" aligns better with the nearby controls. Looks good.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)

1-1: Add "use client" directive — server/client boundary bug

This component uses state/effects and onClick handlers, so it must be a client component per our guidelines for apps/web/app/**/*.tsx. Without "use client", this will error at runtime/build.

Apply at the top of the file:

+ "use client";
  import { useCallback, useEffect, useState } from "react";
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)

200-207: Fix dialog result wiring to avoid premature “success” UI

You’re always passing result={[]} with a TODO. This triggers the success branch in ProcessingPromptFileDialog (empty arrays are truthy). Pass null until you have real created-rule items, or remove this dialog until the new CreatedRulesModal flow is fully integrated.

-      <ProcessingPromptFileDialog
-        open={isDialogOpen}
-        result={[]} // TODO: if we revert back to this component we need to fix this
+      <ProcessingPromptFileDialog
+        open={isDialogOpen}
+        result={null}
         onOpenChange={setIsDialogOpen}
         setViewedProcessingPromptFileDialog={
           setViewedProcessingPromptFileDialog
         }
       />
♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)

76-83: Make clickable Card keyboard-activatable (Enter/Space)

Role and tabIndex are present, but there’s no keyboard handler. Add onKeyDown to meet accessibility guidelines. This mirrors a prior comment on earlier commits.

   <Card
     key={rule.id}
     role="button"
     tabIndex={0}
     className="p-4 hover:shadow-md transition-shadow cursor-pointer"
-    onClick={() => ruleDialog.open({ ruleId: rule.id })}
+    onClick={() => ruleDialog.open({ ruleId: rule.id })}
+    onKeyDown={(e) => {
+      if (e.key === "Enter" || e.key === " ") {
+        e.preventDefault();
+        ruleDialog.open({ ruleId: rule.id });
+      }
+    }}
   >
🧹 Nitpick comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)

135-141: Make the large illustration responsive

The image width/height jumped to 800x600 while the Dialog is max-w-xl (~576px). Add responsive sizing to prevent overflow and improve layout on small screens.

-      <Image
+      <Image
         src="/images/assistant/rules.png"
         alt="Analyzing prompt file"
-        width={800}
-        height={600}
-        className="rounded-lg shadow"
+        width={800}
+        height={600}
+        sizes="(max-width: 640px) 100vw, 576px"
+        className="rounded-lg shadow w-full h-auto"
       />

216-274: Remove large block of commented-out code

The legacy FinalStepReady block is fully commented. Delete or move to a gist to reduce noise. If you plan to restore it, add a TODO with an issue link.

-// function FinalStepReady({
-//   back,
-//   next,
-//   result,
-// }: StepProps & {
-//   result: ResultProps;
-// }) {
-//   const { emailAccountId } = useAccount();
-//   function getDescription() {
-//     let message = "";
-//     ...
-//   }
-//   return ( ... );
-// }
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)

119-124: Remove unused “result” state (counts) to reduce confusion

This state is set but never used after the UI shift to CreatedRulesContent. Drop it and the setter call in the toast success block.

-  const [result, setResult] = useState<{
-    createdRules: number;
-    editedRules: number;
-    removedRules: number;
-  }>();
...
-        const {
-          createdRules = 0,
-          editedRules = 0,
-          removedRules = 0,
-        } = result?.data || {};
-        setResult({ createdRules, editedRules, removedRules });
+        const {
+          createdRules = 0,
+          editedRules = 0,
+          removedRules = 0,
+        } = result?.data || {};

Also applies to: 171-172


163-187: Toast handling is acceptable; consider standardizing on toastError/toastSuccess

You’re using toast.promise here and toastError elsewhere. For consistency with our guidelines, consider using toastError for failure (success toast optional) when result?.serverError is present.

If you want, I can provide a small helper to normalize API-action toast patterns.

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

66-71: Handle zero-rule case more gracefully

If rules is an empty array, the description renders “0 rules have been created…”, which reads odd. Provide a clearer zero case.

-          <DialogDescription>
-            {rules.length === 1
-              ? "Your rule has been created. You can now test it or view the details below."
-              : `${rules.length} rules have been created. You can now test them or view the details below.`}
-          </DialogDescription>
+          <DialogDescription>
+            {rules.length === 0
+              ? "No new rules were created."
+              : rules.length === 1
+                ? "Your rule has been created. You can now test it or view the details below."
+                : `${rules.length} rules have been created. You can now test them or view the details below.`}
+          </DialogDescription>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 530bbe2 and 82a5e18.

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
🧠 Learnings (9)
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Make static elements with click handlers use a valid role attribute.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Make elements with interactive roles and handlers focusable.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Use valid, non-abstract ARIA roles for elements with ARIA roles.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Don't assign non-interactive ARIA roles to interactive HTML elements.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{jsx,tsx} : Use semantic elements instead of role attributes in JSX.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Don't assign interactive ARIA roles to non-interactive HTML elements.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
📚 Learning: 2025-07-18T15:04:44.818Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling; success toast is optional

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx
🧬 Code graph analysis (3)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (7)
apps/web/utils/rule/types.ts (1)
  • CreateRuleResult (3-5)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/utils/path.ts (1)
  • prefixPath (1-4)
apps/web/utils/condition.ts (1)
  • conditionsToString (194-227)
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
  • ActionBadges (495-525)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
  • RuleDialog (23-81)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
  • Examples (53-53)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)
apps/web/utils/rule/types.ts (1)
  • CreateRuleResult (3-5)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
  • CreatedRulesContent (42-121)
🔇 Additional comments (8)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (4)

35-37: Confirm new prop contract across call sites

Prop type is now result: CreateRuleResult[] | null and required. Ensure all callers either pass null until real data is available or a non-empty array when creation completes. Passing [] causes the false-positive success flow noted above.

Would you like me to scan the repo for all ProcessingPromptFileDialog usages and patch them to pass null until results are fetched?


95-100: Header layout copy changes look good

Centering header and constraining description width improves readability, no functional concerns.


113-116: Intro copy change LGTM

Shorter “Creating rules...” copy is clearer and avoids implying a fixed duration.


56-56: Dialog width tweak is fine

max-w-xl aligns with the content density here.

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

69-76: Provider plumbing into RulesPromptForm and Examples looks correct

Passing provider down and letting Examples fetch provider-aware prompts matches the new flow.

Also applies to: 95-113, 347-349


314-322: Good switch to toastError for generate failure

This aligns with our “result?.serverError with toastError” guideline for user-visible errors.

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

24-39: Modal wrapper normalizes null to [] — OK as long as zero-case is handled

Converting rules to an empty array keeps the content simple. With the improved zero messaging, this is fine.


58-112: Overall structure and navigation logic LGTM

Scoped max sizes, success header, list of rules with actions, and “Test Rules” navigation via prefixPath are coherent and follow our UI patterns.

Comment on lines 62 to 73
{currentStep >= STEPS &&
(result ? (
<FinalStepReady
back={back}
next={() => onOpenChange(false)}
result={result}
/>
// <FinalStepReady
// back={back}
// next={() => onOpenChange(false)}
// result={result}
// />

<CreatedRulesContent rules={result} onOpenChange={onOpenChange} />
) : (
<FinalStepWaiting back={back} />
))}
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

Guard final step on non-empty results to avoid false “success”

The condition treats an empty array as truthy, so you’ll render CreatedRulesContent even when there are 0 rules (e.g., callers passing []). Use a length check.

-        {currentStep >= STEPS &&
-          (result ? (
+        {currentStep >= STEPS &&
+          (Array.isArray(result) && result.length > 0 ? (
             // <FinalStepReady
             //   back={back}
             //   next={() => onOpenChange(false)}
             //   result={result}
             // />
-
             <CreatedRulesContent rules={result} onOpenChange={onOpenChange} />
           ) : (
             <FinalStepWaiting back={back} />
           ))}
📝 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
{currentStep >= STEPS &&
(result ? (
<FinalStepReady
back={back}
next={() => onOpenChange(false)}
result={result}
/>
// <FinalStepReady
// back={back}
// next={() => onOpenChange(false)}
// result={result}
// />
<CreatedRulesContent rules={result} onOpenChange={onOpenChange} />
) : (
<FinalStepWaiting back={back} />
))}
{currentStep >= STEPS &&
(Array.isArray(result) && result.length > 0 ? (
// <FinalStepReady
// back={back}
// next={() => onOpenChange(false)}
// result={result}
// />
<CreatedRulesContent rules={result} onOpenChange={onOpenChange} />
) : (
<FinalStepWaiting back={back} />
))}
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
around lines 62 to 73, the final-step rendering treats an empty array as truthy
and will render CreatedRulesContent for [] — change the guard to explicitly
check for a non-empty result (e.g., result && result.length > 0 or
result?.length > 0) so CreatedRulesContent is only rendered when there is at
least one rule; otherwise render FinalStepWaiting.

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)/[emailAccountId]/assistant/RulesPromptNew.tsx (4)

96-104: Tighten editor validation: remove unnecessary typeof check and guard whitespace-only input

getMarkdown() already returns a string. The typeof check is dead code and doesn’t prevent empty/whitespace submissions. Use a trimmed check only.

-  const onSubmit = useCallback(async () => {
-    const markdown = editorRef.current?.getMarkdown();
-    if (typeof markdown !== "string") return;
-    if (markdown.trim() === "") {
-      toastError({
-        description: "Please enter a prompt to create rules",
-      });
-      return;
-    }
+  const onSubmit = useCallback(async () => {
+    const markdown = editorRef.current?.getMarkdown() ?? "";
+    if (!markdown.trim()) {
+      toastError({
+        description: "Please enter a prompt to create rules",
+      });
+      return;
+    }

110-123: Always reset submitting state; await mutate for consistency

Ensure setIsSubmitting(false) runs even if createRulesAction or mutate throws, and await mutate() so list refresh completes before success resolves.

-    toast.promise(
-      async () => {
-        const result = await createRulesAction(emailAccountId, {
-          prompt: markdown,
-        }).finally(() => {
-          setIsSubmitting(false);
-        });
-
-        if (result?.serverError) throw new Error(result.serverError);
-
-        mutate();
-
-        return result;
-      },
+    toast.promise(
+      async () => {
+        try {
+          const result = await createRulesAction(emailAccountId, {
+            prompt: markdown,
+          });
+          if (result?.serverError) throw new Error(result.serverError);
+          await mutate();
+          return result;
+        } finally {
+          setIsSubmitting(false);
+        }
+      },

170-177: Non-submit buttons inside a form must set type="button"

Prevents accidental form submission; also aligns with our accessibility guideline.

           <Button
+            type="button"
             variant="ghost"
             size="sm"
             onClick={() => ruleDialog.open()}
             Icon={PlusIcon}
           >
@@
-              <Button variant="outline" size="sm" onClick={onOpenPersonaDialog}>
+              <Button type="button" variant="outline" size="sm" onClick={onOpenPersonaDialog}>
                 <UserPenIcon className="mr-2 size-4" />
                 Choose persona
               </Button>
@@
-              <Button
+              <Button
+                type="button"
                 variant="outline"
                 size="sm"
                 onClick={() => setIsExamplesOpen((show) => !show)}
               >

Also applies to: 200-203, 205-209


148-156: Auto-open CreatedRulesModal after first-time walkthrough closes

After the first-time Processing dialog is closed, open the CreatedRulesModal if rules exist. This completes the flow reliably.

   useEffect(() => {
     if (!personaPrompt) return;
     editorRef.current?.appendText(personaPrompt);
   }, [personaPrompt]);
 
+  // When the processing walkthrough is closed and we have results,
+  // auto-open the CreatedRulesModal for first-time users.
+  useEffect(() => {
+    if (
+      !isProcessingDialogOpen &&
+      (createdRules?.length ?? 0) > 0 &&
+      !showCreatedRulesModal
+    ) {
+      setShowCreatedRulesModal(true);
+    }
+  }, [isProcessingDialogOpen, createdRules, showCreatedRulesModal]);
🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)

76-78: Surface labels loading error in UI via LoadingContent

Per guidelines, pass both loading and error to LoadingContent.

-  const { userLabels, isLoading: isLoadingLabels } = useLabels();
+  const {
+    userLabels,
+    isLoading: isLoadingLabels,
+    error: labelsError,
+  } = useLabels();
@@
-            <LoadingContent
-              loading={isLoadingLabels}
-              loadingComponent={<Skeleton className="min-h-[70px] w-full" />}
-            >
+            <LoadingContent
+              loading={isLoadingLabels}
+              error={labelsError}
+              loadingComponent={<Skeleton className="min-h-[70px] w-full" />}
+            >

Also applies to: 181-185


60-61: Optional: trigger persona picker after onboarding completes

Restores the previous UX where onboarding leads users directly into persona selection. Safe, minimal change.

-      <AssistantOnboarding />
+      <AssistantOnboarding onComplete={onOpenPersonaDialog} />
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 82a5e18 and 9a2eb20.

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

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

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

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

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

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

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

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
🧠 Learnings (4)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system and user prompts separate; system defines role/task, user contains data/context

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
📚 Learning: 2025-07-18T15:04:57.115Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.115Z
Learning: Applies to **/*.tsx : Validate form inputs before submission

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*Form.tsx : Use `result?.serverError` with `toastError` and `toastSuccess` for error and success notifications in form submission handlers.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{html,jsx,tsx} : Always include a type attribute for button elements.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (12)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
  • RulesPrompt (39-93)
  • RulesPromptForm (99-367)
apps/web/hooks/useModal.tsx (1)
  • useModal (3-9)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
  • getPersonas (85-348)
apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
  • PersonaDialog (7-40)
apps/web/hooks/useRules.tsx (1)
  • useRules (4-8)
apps/web/hooks/useLabels.ts (1)
  • useLabels (62-88)
apps/web/hooks/useDialogState.ts (1)
  • useDialogState (8-32)
apps/web/components/editor/SimpleRichTextEditor.tsx (2)
  • SimpleRichTextEditorRef (22-25)
  • SimpleRichTextEditor (27-153)
apps/web/utils/actions/ai-rule.ts (1)
  • createRulesAction (516-586)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
  • RuleDialog (23-81)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
  • ProcessingPromptFileDialog (27-77)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
  • CreatedRulesModal (24-40)

@elie222 elie222 merged commit 12248ee into main Aug 25, 2025
12 checks passed
@elie222 elie222 deleted the feat/new-rules-layout branch August 25, 2025 13:27
@coderabbitai coderabbitai bot mentioned this pull request Sep 7, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments