Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
869e6f4
assistant onboarding skeleton
elie222 Mar 29, 2025
d1ab915
add skip button. adjust copy
elie222 Mar 29, 2025
96312b1
set category form
elie222 Mar 29, 2025
0441992
move to own file
elie222 Mar 29, 2025
c469d6f
use actions instead of api routes for cold email settings
elie222 Mar 29, 2025
1c46bfb
save cold email setting and basic rules
elie222 Mar 29, 2025
4d25ab7
Merge branch 'main' into assistant-onboarding
elie222 Mar 29, 2025
f2f72a2
Merge branch 'main' into assistant-onboarding
elie222 Mar 29, 2025
d1c0e26
Merge branch 'main' into assistant-onboarding
elie222 Mar 30, 2025
8741e2b
enable reply zero in assistant setup
elie222 Mar 30, 2025
fe9f5a3
styling
elie222 Mar 30, 2025
36fd96b
create rules after onboarding
elie222 Mar 30, 2025
5334a40
Enable draft replies
elie222 Mar 30, 2025
cd2d8dc
update via onboarding
elie222 Mar 30, 2025
a8afcdf
jump to next step faster
elie222 Mar 30, 2025
b22d555
adjust copy
elie222 Mar 30, 2025
ec560dc
redirect to assistant onboarding
elie222 Mar 30, 2025
f2c4d32
hide show me around sooner
elie222 Mar 30, 2025
abb8c54
add set up link
elie222 Mar 30, 2025
73fdc47
scan old emails if none in reply tracker
elie222 Mar 30, 2025
0d64d27
warn
elie222 Mar 30, 2025
f454c39
skip processed emails
elie222 Mar 30, 2025
97efd72
hide scan history button
elie222 Mar 30, 2025
e4ed0ca
process emails when enabling reply zero
elie222 Mar 30, 2025
6b1f596
skip loading reply zero if already enabled
elie222 Mar 30, 2025
6b10579
fix api key for process previous
elie222 Mar 30, 2025
8621ade
adjust initial set up screen
elie222 Mar 30, 2025
1325c26
fix enabled check
elie222 Mar 30, 2025
6d003dc
adjust onboarding link
elie222 Mar 30, 2025
96ee418
fix error
elie222 Mar 30, 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
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function ProcessingPromptFileDialog({
}, []);

useEffect(() => {
if (currentStep >= STEPS) {
if (currentStep > 0) {
setViewedProcessingPromptFileDialog(true);
}
}, [currentStep, setViewedProcessingPromptFileDialog]);
Expand Down
1 change: 1 addition & 0 deletions apps/web/app/(app)/automation/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ONBOARDING_COOKIE_NAME = "viewed_onboarding";
187 changes: 187 additions & 0 deletions apps/web/app/(app)/automation/onboarding/CategoriesSetup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"use client";

import { useCallback } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import type { ControllerRenderProps } from "react-hook-form";
import {
Mail,
Newspaper,
Megaphone,
Calendar,
Receipt,
Bell,
Users,
} from "lucide-react";
import { TypographyH3, TypographyP } from "@/components/Typography";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { createRulesOnboardingAction } from "@/utils/actions/rule";
import { isActionError } from "@/utils/error";
import { toastError } from "@/components/Toast";
import {
createRulesOnboardingBody,
type CreateRulesOnboardingBody,
} from "@/utils/actions/rule.validation";

const NEXT_URL = "/automation/onboarding/draft-replies";

export function CategoriesSetup() {
const router = useRouter();

const form = useForm<CreateRulesOnboardingBody>({
resolver: zodResolver(createRulesOnboardingBody),
defaultValues: {
toReply: "label",
newsletters: "label",
marketing: "label_archive",
calendar: "label",
receipts: "label",
notifications: "label",
coldEmails: "label_archive",
},
});

const onSubmit = useCallback(
async (data: CreateRulesOnboardingBody) => {
// runs in background so we can move on to next step faster
createRulesOnboardingAction(data);
router.push(NEXT_URL);
},
[router],
);

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<TypographyH3 className="mt-2">Set up your assistant</TypographyH3>

<TypographyP className="mt-2">
Choose how you want your emails organized.
<br />
You can add custom categories and rules later.
</TypographyP>

<div className="mt-4 grid grid-cols-1 gap-4">
<CategoryCard
label="To Reply"
id="toReply"
icon={<Mail className="h-5 w-5 text-blue-500" />}
form={form}
/>
<CategoryCard
label="Newsletters"
id="newsletters"
icon={<Newspaper className="h-5 w-5 text-purple-500" />}
form={form}
/>
<CategoryCard
label="Marketing"
id="marketing"
icon={<Megaphone className="h-5 w-5 text-green-500" />}
form={form}
/>
<CategoryCard
label="Calendar"
id="calendar"
icon={<Calendar className="h-5 w-5 text-yellow-500" />}
form={form}
/>
<CategoryCard
label="Receipts"
id="receipts"
icon={<Receipt className="h-5 w-5 text-orange-500" />}
form={form}
/>
<CategoryCard
label="Notifications"
id="notifications"
icon={<Bell className="h-5 w-5 text-red-500" />}
form={form}
/>
<CategoryCard
label="Cold Emails"
id="coldEmails"
icon={<Users className="h-5 w-5 text-indigo-500" />}
form={form}
/>
</div>

<div className="mt-6 flex flex-col gap-2">
<Button type="submit" className="w-full" size="lg">
Next
</Button>

<Button className="w-full" size="lg" variant="outline" asChild>
<Link href={NEXT_URL}>Skip</Link>
</Button>
</div>
</form>
</Form>
);
}

function CategoryCard({
id,
label,
icon,
form,
}: {
id: keyof CreateRulesOnboardingBody;
label: string;
icon: React.ReactNode;
form: ReturnType<typeof useForm<CreateRulesOnboardingBody>>;
}) {
return (
<Card>
<CardContent className="flex items-center gap-4 p-4">
{icon}
<div className="flex-1">{label}</div>
<div className="ml-auto flex items-center gap-4">
<FormField
control={form.control}
name={id}
render={({
field,
}: {
field: ControllerRenderProps<
CreateRulesOnboardingBody,
keyof CreateRulesOnboardingBody
>;
}) => (
<FormItem>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger className="w-[180px]">
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="label">Label</SelectItem>
<SelectItem value="label_archive">
Label + Skip Inbox
</SelectItem>
<SelectItem value="none">None</SelectItem>
</SelectContent>
</Select>
</FormItem>
)}
/>
</div>
</CardContent>
</Card>
);
}
52 changes: 52 additions & 0 deletions apps/web/app/(app)/automation/onboarding/completed/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import Link from "next/link";
import { Check } from "lucide-react";
import { TypographyH3, TypographyP } from "@/components/Typography";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";

export default function CompletedPage() {
return (
<div>
<Card className="my-4 max-w-2xl p-6 sm:mx-4 md:mx-auto">
<div className="text-center">
<div className="mx-auto mb-6 flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<Check className="h-6 w-6 text-green-600" />
</div>

<TypographyH3>You're all set!</TypographyH3>

<div className="mt-6 space-y-4">
<TypographyP>
We've configured your inbox with smart defaults to help you stay
organized. Your emails will be automatically categorized, and
we'll draft replies that match your writing style.
</TypographyP>

<TypographyP>
Want to customize further? You can create custom rules, and
fine-tune your preferences anytime.
</TypographyP>
</div>

<div className="mt-8 flex flex-col gap-4">
<Button size="lg" asChild>
<Link href="/automation">Go to AI Assistant</Link>
</Button>

{/* <Button variant="outline" size="lg" asChild>
<Link
href="/automation/rules"
className="flex items-center gap-2"
>
<Settings className="h-4 w-4" />
Customize Rules
</Link>
</Button> */}
</div>
</div>
</Card>
</div>
);
}
67 changes: 67 additions & 0 deletions apps/web/app/(app)/automation/onboarding/draft-replies/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import { useCallback } from "react";
import { useRouter } from "next/navigation";
import { Card } from "@/components/ui/card";
import { TypographyH3, TypographyP } from "@/components/Typography";
import { ButtonListSurvey } from "@/components/ButtonListSurvey";
import { enableDraftRepliesAction } from "@/utils/actions/rule";
import { isActionError } from "@/utils/error";
import { toastError } from "@/components/Toast";
import { ONBOARDING_COOKIE_NAME } from "@/app/(app)/automation/consts";

export default function DraftRepliesPage() {
const router = useRouter();

const onSetDraftReplies = useCallback(
async (value: string) => {
if (value === "yes") {
const result = await enableDraftRepliesAction({ enable: true });

if (isActionError(result)) {
toastError({
description: "There was an error enabling draft replies",
});
}
}

// This sets the cookie to keep the sidebar state.
document.cookie = `${ONBOARDING_COOKIE_NAME}=true; path=/; max-age=${Number.MAX_SAFE_INTEGER}`;

router.push("/automation/onboarding/completed");
},
[router],
);

return (
<div>
<Card className="my-4 max-w-2xl p-6 sm:mx-4 md:mx-auto">
<div className="text-center">
<TypographyH3 className="mx-auto max-w-lg">
Would you like AI to draft your email replies?
</TypographyH3>

<TypographyP className="mx-auto mt-4 max-w-sm text-muted-foreground">
AI will match your writing style and suggest drafts in Gmail. You
control when and what to send.
</TypographyP>

<ButtonListSurvey
className="mt-6"
options={[
{
label: "Yes, draft replies",
value: "yes",
},
{
label: "No thanks",
value: "no",
},
]}
onClick={onSetDraftReplies}
/>
</div>
</Card>
</div>
);
}
10 changes: 10 additions & 0 deletions apps/web/app/(app)/automation/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Card } from "@/components/ui/card";
import { CategoriesSetup } from "./CategoriesSetup";

export default function OnboardingPage() {
return (
<Card className="my-4 w-full max-w-2xl p-6 sm:mx-4 md:mx-auto">
<CategoriesSetup />
</Card>
);
}
Loading