Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b377eff
Inline group view. Comment out old group code
elie222 Jan 28, 2025
eb4ec48
Delete old code related to creating and generating groups with ai
elie222 Jan 28, 2025
7927da6
Add group items. Delete more old group code
elie222 Jan 28, 2025
7d7e8ce
cascade delete group when deleting rule
elie222 Jan 28, 2025
d5eb2c7
delete detached rules
elie222 Jan 28, 2025
3cb29c8
remove unused imports
elie222 Jan 28, 2025
7022b98
extract createnewsletter/receipt logic
elie222 Jan 28, 2025
5391a7a
delete group upon deleting rule
elie222 Jan 28, 2025
abdf732
Fix create predefined group error?
elie222 Jan 28, 2025
d1879ba
Adjust copy for rules condition
elie222 Jan 28, 2025
b54f0ff
Add toggle for auto add patterns
elie222 Jan 28, 2025
7fe7e06
Add group item status
elie222 Jan 28, 2025
cef80f3
Merge branch 'main' into autoupdating-groups
elie222 Jan 28, 2025
cc5c155
Change groups to learned patterns. Move out of conditions
elie222 Jan 29, 2025
3b96e1b
disable auto learn toggle
elie222 Jan 29, 2025
c3b8ad2
Revert group item status update
elie222 Jan 29, 2025
782d68c
Revert back to trash icon to remove from group
elie222 Jan 29, 2025
819ba38
Ignore conditional operator for group
elie222 Jan 29, 2025
3cc8eb1
Clean out a lot of the old groups code
elie222 Jan 29, 2025
4f0ec4c
Remove add to group for AI assistant. Switch naming to patterns inste…
elie222 Jan 29, 2025
2ae5a9e
update color and copy
elie222 Jan 29, 2025
c9ea2a9
Adjust smart cat copy
elie222 Jan 29, 2025
7d6befc
Remove group from prompt to rules
elie222 Jan 29, 2025
e14da21
adjust prompts
elie222 Jan 29, 2025
a86f1ac
Add more to assistant prompt
elie222 Jan 29, 2025
45e3902
Fix tests
elie222 Jan 29, 2025
0d3e1ab
fix broken group logic
elie222 Jan 29, 2025
580ba0b
Fix tests
elie222 Jan 29, 2025
737128a
fix turbo json
elie222 Jan 29, 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
148 changes: 29 additions & 119 deletions apps/web/app/(app)/automation/RuleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import useSWR from "swr";
import {
type FieldError,
type FieldErrors,
type SubmitHandler,
type UseFormRegisterReturn,
type UseFormSetValue,
useFieldArray,
useForm,
} from "react-hook-form";
Expand All @@ -23,11 +19,7 @@ import { Card } from "@/components/Card";
import { Button } from "@/components/ui/button";
import { ErrorMessage, Input, Label } from "@/components/Input";
import { toastError, toastSuccess } from "@/components/Toast";
import {
MessageText,
SectionDescription,
TypographyH3,
} from "@/components/Typography";
import { SectionDescription, TypographyH3 } from "@/components/Typography";
import {
ActionType,
CategoryFilterType,
Expand All @@ -42,16 +34,8 @@ import {
import { actionInputs } from "@/utils/action-item";
import { Select } from "@/components/Select";
import { Toggle } from "@/components/Toggle";
import type { GroupsResponse } from "@/app/api/user/group/route";
import { LoadingContent } from "@/components/LoadingContent";
import { TooltipExplanation } from "@/components/TooltipExplanation";
import { ViewGroupButton } from "@/app/(app)/automation/group/ViewGroup";
import { CreateGroupModalButton } from "@/app/(app)/automation/group/CreateGroupModal";
import { createPredefinedGroupAction } from "@/utils/actions/group";
import {
NEWSLETTER_GROUP_ID,
RECEIPT_GROUP_ID,
} from "@/app/(app)/automation/create/examples";
import { isActionError } from "@/utils/error";
import { Combobox } from "@/components/Combobox";
import { useLabels } from "@/hooks/useLabels";
Expand All @@ -70,6 +54,7 @@ import {
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { LearnedPatterns } from "@/app/(app)/automation/group/LearnedPatterns";

export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
const {
Expand All @@ -78,7 +63,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
watch,
setValue,
control,
formState: { errors, isSubmitting },
formState: { errors, isSubmitting, isSubmitted },
trigger,
} = useForm<CreateRuleBody>({
resolver: zodResolver(createRuleBody),
Expand Down Expand Up @@ -168,7 +153,9 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
const conditions = watch("conditions");
const unusedCondition = useMemo(() => {
const usedConditions = new Set(conditions?.map(({ type }) => type));
return Object.values(RuleType).find((type) => !usedConditions.has(type));
return [RuleType.AI, RuleType.STATIC, RuleType.CATEGORY].find(
(type) => !usedConditions.has(type),
) as Exclude<RuleType, "GROUP"> | undefined;
}, [conditions]);

// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Expand All @@ -192,6 +179,21 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {

return (
<form onSubmit={handleSubmit(onSubmit)}>
{isSubmitted && Object.keys(errors).length > 0 && (
<div className="mt-4">
<AlertError
title="Error"
description={
<ul className="list-disc">
{Object.values(errors).map((error) => (
<li key={error.message}>{error.message}</li>
))}
</ul>
}
/>
</div>
)}

<div className="mt-4">
<Input
type="text"
Expand Down Expand Up @@ -254,7 +256,6 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
options={[
{ label: "AI", value: RuleType.AI },
{ label: "Static", value: RuleType.STATIC },
{ label: "Group", value: RuleType.GROUP },
{ label: "Smart Category", value: RuleType.CATEGORY },
]}
error={
Expand Down Expand Up @@ -317,7 +318,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
(errors.conditions?.[index] as { from?: FieldError })
?.from
}
placeholder="e.g. elie@getinboxzero.com"
placeholder="e.g. hello@company.com"
tooltipText="Only apply this rule to emails from this address."
/>
<Input
Expand All @@ -328,7 +329,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
error={
(errors.conditions?.[index] as { to?: FieldError })?.to
}
placeholder="e.g. elie@getinboxzero.com"
placeholder="e.g. hello@company.com"
tooltipText="Only apply this rule to emails sent to this address."
/>
<Input
Expand All @@ -349,16 +350,6 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
</>
)}

{watch(`conditions.${index}.type`) === RuleType.GROUP && (
<GroupsTab
registerProps={register(`conditions.${index}.groupId`)}
setValue={setValue}
errors={errors}
groupId={watch(`conditions.${index}.groupId`)}
ruleId={rule.id}
/>
)}

{watch(`conditions.${index}.type`) === RuleType.CATEGORY && (
<>
<div className="flex items-center gap-4">
Expand Down Expand Up @@ -498,6 +489,12 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
</div>
)}

{rule.groupId && (
<div className="mt-4">
<LearnedPatterns groupId={rule.groupId} />
</div>
)}

<TypographyH3 className="mt-6">Actions</TypographyH3>

{actionErrors.length > 0 && (
Expand Down Expand Up @@ -717,93 +714,6 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
);
}

function GroupsTab(props: {
registerProps: UseFormRegisterReturn;
setValue: UseFormSetValue<CreateRuleBody>;
errors: FieldErrors<CreateRuleBody>;
groupId?: string | null;
ruleId?: string | null;
}) {
const { setValue, ruleId } = props;
const { data, isLoading, error, mutate } =
useSWR<GroupsResponse>("/api/user/group");
const [loadingCreateGroup, setLoadingCreateGroup] = useState(false);

useEffect(() => {
async function createGroup(groupId: string) {
setLoadingCreateGroup(true);

const result = await createPredefinedGroupAction(groupId);

if (isActionError(result)) {
toastError({ description: result.error });
} else if (!result) {
toastError({ description: "Error creating group" });
} else {
mutate();
setValue("conditions", [{ groupId: result.id, type: RuleType.GROUP }]);
}

setLoadingCreateGroup(false);
}

if (
props.groupId === NEWSLETTER_GROUP_ID ||
props.groupId === RECEIPT_GROUP_ID
) {
createGroup(props.groupId);
}
}, [mutate, props.groupId, setValue]);

return (
<div className="mt-4">
<SectionDescription>
A group is a static collection of senders or subjects.
</SectionDescription>

{loadingCreateGroup && (
<MessageText className="my-4 text-center">
Creating group with AI... This will take up to 30 seconds.
</MessageText>
)}

<LoadingContent loading={isLoading || loadingCreateGroup} error={error}>
<div className="mt-2 grid gap-2 sm:flex sm:items-center">
{data?.groups && data?.groups.length > 0 && (
<div className="min-w-[250px] flex-1">
<Select
label=""
options={data.groups.map((group) => ({
label: `${group.name}${group.rule && group.rule.id !== ruleId ? " (already in use)" : ""}`,
value: group.id,
}))}
{...props.registerProps}
// TODO: fix this
// error={props.errors.groupId}
/>
</div>
)}

{props.groupId && (
<ViewGroupButton
groupId={props.groupId}
ButtonComponent={({ onClick }) => (
<Button variant="outline" onClick={onClick}>
View
</Button>
)}
/>
)}
<CreateGroupModalButton
existingGroups={data?.groups.map((group) => group.name) || []}
buttonVariant="outline"
/>
</div>
</LoadingContent>
</div>
);
}

function LabelCombobox({
value,
onChangeValue,
Expand Down
11 changes: 4 additions & 7 deletions apps/web/app/(app)/automation/create/examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import {
PresentationIcon,
} from "lucide-react";

export const RECEIPT_GROUP_ID = "RECEIPT";
export const NEWSLETTER_GROUP_ID = "NEWSLETTER";

export const examples: {
title: string;
description: string;
Expand All @@ -28,8 +25,8 @@ export const examples: {
name: "Forward receipts",
conditions: [
{
type: RuleType.GROUP,
groupId: RECEIPT_GROUP_ID,
type: RuleType.AI,
instructions: "Forward receipts to alice@accountant.com.",
},
],
actions: [
Expand All @@ -45,8 +42,8 @@ export const examples: {
name: "Archive and label newsletters",
conditions: [
{
type: RuleType.GROUP,
groupId: NEWSLETTER_GROUP_ID,
type: RuleType.AI,
instructions: "Archive newsletters and label them as 'Newsletter'.",
},
],
actions: [
Expand Down
Loading