From c620497ede93673151272d25e8da2b23fc409558 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Wed, 16 Jul 2025 13:57:54 +0300 Subject: [PATCH 1/2] Learned patterns dialog --- .../[emailAccountId]/assistant/RuleForm.tsx | 58 +++------------ .../assistant/group/LearnedPatterns.tsx | 71 ++++++++++++++++++- version.txt | 2 +- 3 files changed, 81 insertions(+), 50 deletions(-) diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx index df6496d438..8232efc3e2 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx @@ -64,9 +64,7 @@ import { DropdownMenuRadioItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { LearnedPatterns } from "@/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns"; -import { Tooltip } from "@/components/Tooltip"; -import { createGroupAction } from "@/utils/actions/group"; +import { LearnedPatternsDialog } from "@/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns"; import { NEEDS_REPLY_LABEL_NAME } from "@/utils/reply-tracker/consts"; import { Badge } from "@/components/Badge"; import { useAccount } from "@/providers/EmailAccountProvider"; @@ -300,11 +298,6 @@ export function RuleForm({ ]; }, [digestEnabled]); - const [learnedPatternGroupId, setLearnedPatternGroupId] = useState( - rule.groupId, - ); - const [showLearnedPatterns, setShowLearnedPatterns] = useState(false); - const [isNameEditMode, setIsNameEditMode] = useState(alwaysEditMode); const [isConditionsEditMode, setIsConditionsEditMode] = useState(alwaysEditMode); @@ -401,40 +394,6 @@ export function RuleForm({ )} - {!!rule.id && ( - - { - if (!rule.id) return; - - setShowLearnedPatterns((b) => !b); - if (learnedPatternGroupId) return; - - const result = await createGroupAction(emailAccountId, { - ruleId: rule.id, - }); - - if (result?.serverError) { - toastError({ description: result.serverError }); - } else if (!result?.data?.groupId) { - toastError({ - description: - "There was an error setting up learned patterns.", - }); - } else { - setLearnedPatternGroupId(result.data.groupId); - setShowLearnedPatterns(true); - } - }} - > - - - - )} - {!alwaysEditMode && ( )} - {showLearnedPatterns && learnedPatternGroupId && ( - - - - )} - Actions {!alwaysEditMode && ( @@ -896,6 +849,15 @@ export function RuleForm({ /> + {!!rule.id && ( + + + + )} + {rule.id && ( (groupId); + + return ( + + + { + if (!ruleId) return; + + if (groupId) return; + + const result = await createGroupAction(emailAccountId, { ruleId }); + + if (result?.serverError) { + toastError({ description: result.serverError }); + } else if (!result?.data?.groupId) { + toastError({ + description: "There was an error setting up learned patterns.", + }); + } else { + setLearnedPatternGroupId(result.data.groupId); + } + }} + > + View learned patterns + + + + + + Learned Patterns + + Learned patterns are patterns that the AI has learned from your + email history. When a learned pattern is matched other rules + conditions are skipped and this rule is automatically selected. + + + + {learnedPatternGroupId && } + + + ); +} -export function LearnedPatterns({ groupId }: { groupId: string }) { +function LearnedPatterns({ groupId }: { groupId: string }) { const [isOpen, setIsOpen] = useState(false); return ( diff --git a/version.txt b/version.txt index 3b816664cd..7e96cbf1f7 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v1.9.10 \ No newline at end of file +v1.9.11 \ No newline at end of file From 15ddf349e1b0dcce6e8eafb9c72d66e0a9d496cf Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Wed, 16 Jul 2025 14:41:39 +0300 Subject: [PATCH 2/2] show loading state when setting up learned patterns --- .../assistant/group/LearnedPatterns.tsx | 78 +++++++------------ apps/web/utils/actions/group.validation.ts | 2 +- 2 files changed, 29 insertions(+), 51 deletions(-) diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx index 86a6829af4..22255fad79 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx @@ -1,12 +1,7 @@ "use client"; import { useState } from "react"; -import { BrainIcon, ChevronDownIcon } from "lucide-react"; -import { - Collapsible, - CollapsibleTrigger, - CollapsibleContent, -} from "@/components/ui/collapsible"; +import { useAction } from "next-safe-action/hooks"; import { ViewGroup } from "@/app/(app)/[emailAccountId]/assistant/group/ViewGroup"; import { Dialog, @@ -20,6 +15,7 @@ import { Button } from "@/components/ui/button"; import { createGroupAction } from "@/utils/actions/group"; import { useAccount } from "@/providers/EmailAccountProvider"; import { toastError } from "@/components/Toast"; +import { Skeleton } from "@/components/ui/skeleton"; export function LearnedPatternsDialog({ ruleId, @@ -34,6 +30,26 @@ export function LearnedPatternsDialog({ string | null >(groupId); + const { execute, isExecuting } = useAction( + createGroupAction.bind(null, emailAccountId), + { + onSuccess: (data) => { + if (data.data?.groupId) { + setLearnedPatternGroupId(data.data.groupId); + } else { + toastError({ + description: "There was an error setting up learned patterns.", + }); + } + }, + onError: (error) => { + toastError({ + description: error.error.serverError || "Unknown error", + }); + }, + }, + ); + return ( @@ -45,17 +61,7 @@ export function LearnedPatternsDialog({ if (groupId) return; - const result = await createGroupAction(emailAccountId, { ruleId }); - - if (result?.serverError) { - toastError({ description: result.serverError }); - } else if (!result?.data?.groupId) { - toastError({ - description: "There was an error setting up learned patterns.", - }); - } else { - setLearnedPatternGroupId(result.data.groupId); - } + execute({ ruleId }); }} > View learned patterns @@ -72,40 +78,12 @@ export function LearnedPatternsDialog({ - {learnedPatternGroupId && } + {isExecuting ? ( + + ) : ( + learnedPatternGroupId && + )} ); } - -function LearnedPatterns({ groupId }: { groupId: string }) { - const [isOpen, setIsOpen] = useState(false); - - return ( - - - - - Learned Patterns - - - - - - - - - - - - ); -} diff --git a/apps/web/utils/actions/group.validation.ts b/apps/web/utils/actions/group.validation.ts index 0f1b3dcbef..db882a6293 100644 --- a/apps/web/utils/actions/group.validation.ts +++ b/apps/web/utils/actions/group.validation.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { GroupItemType } from "@prisma/client"; export const createGroupBody = z.object({ - ruleId: z.string(), + ruleId: z.string().min(1, "Rule ID is required"), }); export type CreateGroupBody = z.infer;