Skip to content
Merged
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
68f7c6a
Add stats cards to setup page
jshwrnr Dec 1, 2025
4f2ed54
Add home to sidebar
jshwrnr Dec 1, 2025
c0d4a2c
Clean up rules table UI
jshwrnr Dec 1, 2025
936599f
Remove unused code
jshwrnr Dec 1, 2025
7a1ed21
Align "Add rule" button to the right side
jshwrnr Dec 1, 2025
29fac52
Initial clean up of Rules creator
jshwrnr Dec 1, 2025
ac5f1ac
Wrap rule conditions in card
jshwrnr Dec 1, 2025
9f4e1f4
Move rule steps to a separate component
jshwrnr Dec 1, 2025
adfba60
Replace match all dropdown
jshwrnr Dec 1, 2025
dbe8fe7
Remove step number in rule steps
jshwrnr Dec 1, 2025
ecc810f
Remove isActionsEditMode
jshwrnr Dec 1, 2025
35c39e1
Add "Where" next to dropdown in rule select
jshwrnr Dec 1, 2025
011e312
Cleaning up the user experience of creating rules
jshwrnr Dec 1, 2025
2cea0fe
Replace text
jshwrnr Dec 1, 2025
3edf40b
Refactor action rule builder
jshwrnr Dec 2, 2025
69c8fac
Add icons to actions select
jshwrnr Dec 2, 2025
ea00e77
Various changes
jshwrnr Dec 3, 2025
6d532d0
Make trash icon lighter gray
jshwrnr Dec 3, 2025
f50f723
Text and icon changes
jshwrnr Dec 3, 2025
92a9d14
Clean up label rule form
jshwrnr Dec 3, 2025
d813ac1
Wrap certain actions in cards and show their labels
jshwrnr Dec 3, 2025
bead928
Show all form content within card
jshwrnr Dec 3, 2025
02b7ede
Show "show all fields" above the fields that are then shown
jshwrnr Dec 3, 2025
ebab99c
Styling fixes for rules modal
jshwrnr Dec 3, 2025
474d30a
field gap fix
jshwrnr Dec 3, 2025
5b70a4a
Fix spacing in rules form
jshwrnr Dec 3, 2025
309b2b3
Update placeholder
jshwrnr Dec 3, 2025
360a7b6
Move tooltips to fields
jshwrnr Dec 3, 2025
b1d660f
Use new delay fields for "Mark read" option
jshwrnr Dec 3, 2025
a9b2b29
Color fix
jshwrnr Dec 3, 2025
3acb5f7
Fix issue when removing an action
jshwrnr Dec 4, 2025
ceddc3e
Add card around webhook action
jshwrnr Dec 4, 2025
d22cbad
Update text
jshwrnr Dec 4, 2025
a3847b6
Merge branch 'main' into feat/rules-improvements
jshwrnr Dec 4, 2025
5c1171c
Fix typo
jshwrnr Dec 4, 2025
4119f65
Fix
jshwrnr Dec 4, 2025
096ea69
Merge branch 'main' into feat/rules-improvements
elie222 Dec 4, 2025
e2fe6fb
Merge branch 'feat/rules-improvements' of https://github.com/jshwrnr/…
jshwrnr Dec 7, 2025
258d6c1
Merge branch 'main' into feat/rules-improvements
jshwrnr Dec 7, 2025
3de9d99
Merge branch 'main' into feat/ux-improvements
jshwrnr Dec 7, 2025
336e9ad
Merge branch 'feat/ux-improvements' into feat/rules-improvements
jshwrnr Dec 7, 2025
b353538
Remove "rules executed" and "inbox coverage" and replace them with "d…
jshwrnr Dec 7, 2025
4e377f8
Merge branch 'feat/ux-improvements' into feat/rules-improvements
jshwrnr Dec 7, 2025
0236d7f
Align action columns to the left instead of right
jshwrnr Dec 7, 2025
dde60f6
Merge branch 'main' into feat/rules-improvements
elie222 Dec 10, 2025
3fb0612
Move advanced rule options behind dropdown menu
jshwrnr Dec 10, 2025
8de5b88
Change copy to "When"
jshwrnr Dec 10, 2025
7d5f421
Merge branch 'main' into feat/rules-improvements
jshwrnr Dec 10, 2025
2a70d9f
Remove conditional title in automation rules
jshwrnr Dec 10, 2025
6c45c95
Spacing fix
jshwrnr Dec 10, 2025
607735d
Change name from "Home" to "Dashboard"
jshwrnr Dec 11, 2025
0646081
Merge branch 'main' into feat/rules-improvements
jshwrnr Dec 11, 2025
4df816e
Undo changes with conditions
jshwrnr Dec 11, 2025
347723a
Allow condition values to be set to null
jshwrnr Dec 11, 2025
6067f44
Merge branch 'main' into feat/rules-improvements
elie222 Dec 15, 2025
81b98f0
refactor
elie222 Dec 15, 2025
20083e8
clean up
elie222 Dec 15, 2025
46dfcd2
uniform button size
elie222 Dec 15, 2025
5a0ebfc
simplify rule form ui
elie222 Dec 15, 2025
b03158c
copy
elie222 Dec 15, 2025
500fcac
Merge branch 'main' into feat/rules-improvements
elie222 Dec 15, 2025
1673fd8
fix up action buttons
elie222 Dec 15, 2025
10208dd
fix up ui when logic
elie222 Dec 15, 2025
7436a3c
simplify ui
elie222 Dec 15, 2025
62f50a1
fixes for dev
elie222 Dec 15, 2025
e30a17f
copy
elie222 Dec 15, 2025
5263376
fix
elie222 Dec 15, 2025
9543c3d
revert changes that arent ready
elie222 Dec 15, 2025
1493fab
styling
elie222 Dec 15, 2025
39d3dc9
fixes
elie222 Dec 15, 2025
6b10a0d
fix paths
elie222 Dec 15, 2025
7357a21
show if a rule has static conditions
elie222 Dec 16, 2025
8bfa7cb
show icon on each row with conditions
elie222 Dec 16, 2025
c1c2e2c
fix
elie222 Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
773 changes: 773 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/ActionSteps.tsx

Large diffs are not rendered by default.

547 changes: 547 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/ConditionSteps.tsx

Large diffs are not rendered by default.

1,422 changes: 269 additions & 1,153 deletions apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/RuleSectionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Card } from "@/components/ui/card";
import { TypographyH3 } from "@/components/Typography";
import { cn } from "@/utils";
Comment thread
elie222 marked this conversation as resolved.

export function RuleSectionCard({
icon: Icon,
color,
title,
errors,
children,
}: {
icon: React.ComponentType<{ className?: string }>;
color: "blue" | "green";
title: string;
errors?: React.ReactNode;
children: React.ReactNode;
}) {
return (
<Card className="rounded-lg p-4">
<div>
<div className="flex items-center gap-3">
<Icon
className={cn("size-5", {
"text-blue-600 dark:text-blue-400": color === "blue",
"text-green-600 dark:text-green-400": color === "green",
})}
/>
<TypographyH3 className="text-base">{title}</TypographyH3>
</div>

{errors && <div className="mt-2">{errors}</div>}

{children && <div className="mt-4 space-y-2">{children}</div>}
</div>
</Card>
);
}
233 changes: 233 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/RuleStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { Button } from "@/components/ui/button";
import {
TrashIcon,
MoreHorizontalIcon,
ClockIcon,
SparklesIcon,
PenLineIcon,
} from "lucide-react";
import { cn } from "@/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
Comment thread
elie222 marked this conversation as resolved.

function DeleteButton({
onClick,
ariaLabel,
}: {
onClick: () => void;
ariaLabel: string;
}) {
return (
<Button
size="icon"
variant="ghost"
className="size-8 mt-1"
aria-label={ariaLabel}
onClick={onClick}
>
<TrashIcon className="size-4 text-muted-foreground" />
</Button>
);
}

function OptionsMenu({
onAddDelay,
onRemoveDelay,
hasDelay,
onUsePrompt,
onUseLabel,
isPromptMode,
onSetManually,
onUseAiDraft,
isManualMode,
}: {
onAddDelay?: () => void;
onRemoveDelay?: () => void;
hasDelay?: boolean;
onUsePrompt?: () => void;
onUseLabel?: () => void;
isPromptMode?: boolean;
onSetManually?: () => void;
onUseAiDraft?: () => void;
isManualMode?: boolean;
}) {
const hasOptions =
onAddDelay ||
onRemoveDelay ||
onUsePrompt ||
onUseLabel ||
onSetManually ||
onUseAiDraft;

if (!hasOptions) return null;

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
variant="ghost"
className="size-8 mt-1"
aria-label="More options"
>
<MoreHorizontalIcon className="size-4 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{onUsePrompt && !isPromptMode && (
<DropdownMenuItem onClick={onUsePrompt}>
<SparklesIcon className="mr-2 size-4" />
Use prompt
</DropdownMenuItem>
)}
{onUseLabel && isPromptMode && (
<DropdownMenuItem onClick={onUseLabel}>
<SparklesIcon className="mr-2 size-4" />
Use label
</DropdownMenuItem>
)}
{onSetManually && !isManualMode && (
<DropdownMenuItem onClick={onSetManually}>
<PenLineIcon className="mr-2 size-4" />
Set content manually
</DropdownMenuItem>
)}
{onUseAiDraft && isManualMode && (
<DropdownMenuItem onClick={onUseAiDraft}>
<SparklesIcon className="mr-2 size-4" />
Use AI draft
</DropdownMenuItem>
)}
{onAddDelay && !hasDelay && (
<DropdownMenuItem onClick={onAddDelay}>
<ClockIcon className="mr-2 size-4" />
Add delay
</DropdownMenuItem>
)}
{onRemoveDelay && hasDelay && (
<DropdownMenuItem onClick={onRemoveDelay}>
<ClockIcon className="mr-2 size-4" />
Remove delay
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}

function ActionButtons({
onRemove,
removeAriaLabel,
onAddDelay,
onRemoveDelay,
hasDelay,
onUsePrompt,
onUseLabel,
isPromptMode,
onSetManually,
onUseAiDraft,
isManualMode,
}: {
onRemove: () => void;
removeAriaLabel: string;
onAddDelay?: () => void;
onRemoveDelay?: () => void;
hasDelay?: boolean;
onUsePrompt?: () => void;
onUseLabel?: () => void;
isPromptMode?: boolean;
onSetManually?: () => void;
onUseAiDraft?: () => void;
isManualMode?: boolean;
}) {
return (
<div className="flex items-start">
<OptionsMenu
onAddDelay={onAddDelay}
onRemoveDelay={onRemoveDelay}
hasDelay={hasDelay}
onUsePrompt={onUsePrompt}
onUseLabel={onUseLabel}
isPromptMode={isPromptMode}
onSetManually={onSetManually}
onUseAiDraft={onUseAiDraft}
isManualMode={isManualMode}
/>
<DeleteButton onClick={onRemove} ariaLabel={removeAriaLabel} />
</div>
);
}

function CardLayout({ children }: { children: React.ReactNode }) {
return <div className="flex flex-col sm:flex-row gap-2">{children}</div>;
}

function CardLayoutRight({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return (
<div className={cn("space-y-2 mx-auto w-full", className)}>{children}</div>
);
}

export function RuleStep({
onRemove,
leftContent,
rightContent,
removeAriaLabel,
onAddDelay,
onRemoveDelay,
hasDelay,
onUsePrompt,
onUseLabel,
isPromptMode,
onSetManually,
onUseAiDraft,
isManualMode,
}: {
onRemove: () => void;
leftContent: React.ReactNode | null;
rightContent: React.ReactNode;
removeAriaLabel: string;
onAddDelay?: () => void;
onRemoveDelay?: () => void;
hasDelay?: boolean;
onUsePrompt?: () => void;
onUseLabel?: () => void;
isPromptMode?: boolean;
onSetManually?: () => void;
onUseAiDraft?: () => void;
isManualMode?: boolean;
}) {
return (
<div className="flex items-start gap-3">
<div className="relative flex-1">
<CardLayout>
{leftContent && <div className="shrink-0">{leftContent}</div>}
<CardLayoutRight>{rightContent}</CardLayoutRight>
<ActionButtons
onRemove={onRemove}
removeAriaLabel={removeAriaLabel}
onAddDelay={onAddDelay}
onRemoveDelay={onRemoveDelay}
hasDelay={hasDelay}
onUsePrompt={onUsePrompt}
onUseLabel={onUseLabel}
isPromptMode={isPromptMode}
onSetManually={onSetManually}
onUseAiDraft={onUseAiDraft}
isManualMode={isManualMode}
/>
</CardLayout>
</div>
</div>
);
}
40 changes: 40 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/RuleSteps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { PlusIcon } from "lucide-react";
import { Tooltip } from "@/components/Tooltip";
import type { ReactNode } from "react";

export function RuleSteps({
children,
onAdd,
addButtonLabel,
addButtonDisabled = false,
addButtonTooltip,
}: {
children: ReactNode;
onAdd: () => void;
addButtonLabel: string;
addButtonDisabled?: boolean;
addButtonTooltip?: string;
}) {
return (
<Card className="p-4 space-y-2 border-none shadow-none bg-gray-50 dark:bg-gray-900">
{children}
<div>
<Tooltip hide={!addButtonTooltip} content={addButtonTooltip || ""}>
<span>
<Button
variant="ghost"
size="sm"
onClick={onAdd}
disabled={addButtonDisabled}
Icon={PlusIcon}
>
{addButtonLabel}
</Button>
</span>
</Tooltip>
</div>
</Card>
);
}
Loading
Loading