Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onboarding #245

Merged
merged 28 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1c65f23
rename onboarding to old
elie222 Oct 13, 2024
ead6124
Add basic resend style onboarding
elie222 Oct 14, 2024
ac06483
Build out more of bulk unsub and ai automation in onboarding flow
elie222 Oct 14, 2024
2f6a034
Merge branch 'main' into onboarding
elie222 Oct 14, 2024
448f883
Improve cold email blocker for onboarding
elie222 Oct 14, 2024
9b6fac7
cold email and onboarding adjustments
elie222 Oct 15, 2024
b88bf60
fetch real data for bulk unsub
elie222 Oct 15, 2024
54364ca
clean up timespans and copy
elie222 Oct 15, 2024
4eac9df
unsub in onboarding
elie222 Oct 16, 2024
ffc7f29
Merge branch 'main' into onboarding
elie222 Oct 18, 2024
928cf7d
Add AI to find example matches for a given rules prompt
elie222 Oct 18, 2024
2ca0d31
ai step for onboarding
elie222 Oct 18, 2024
934a144
show results of test in onboarding
elie222 Oct 20, 2024
90f8698
show ai assistant onboarding results. show video demos
elie222 Oct 20, 2024
741cb95
add onboarding to side nav
elie222 Oct 20, 2024
a006805
show real unsub status in onboarding
elie222 Oct 20, 2024
e906818
mark onboarding completed
elie222 Oct 20, 2024
e1e1387
only send to upgrade if not premium. and other onboarding fixes
elie222 Oct 20, 2024
2b6adfc
adjust pricing to remove free
elie222 Oct 20, 2024
97a8277
book a call link for copilot plan
elie222 Oct 20, 2024
a063bfe
adjust additional seats copilot
elie222 Oct 20, 2024
488ad77
adjust ordering on onboarding
elie222 Oct 20, 2024
5e60deb
clean up onboarding code
elie222 Oct 20, 2024
4df9f51
adjust onboarding copy
elie222 Oct 20, 2024
cdea9da
fix missing react dep
elie222 Oct 20, 2024
378f97b
make coderabbit pr fixes
elie222 Oct 20, 2024
4f9048b
fix up onboarding for long ai input
elie222 Oct 20, 2024
ff24e91
wrap rule actions in middleware
elie222 Oct 20, 2024
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
3 changes: 3 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ You are an expert in TypeScript, Node.js, Next.js App Router, React, Prisma, Pos
- Use React Hook Form with Zod for validation. The same validation should be done in the server action too. Example:

```tsx
import { Input } from "@/components/Input";
import { Button } from "@/components/ui/button";
elie222 marked this conversation as resolved.
Show resolved Hide resolved

export const ProcessHistory = () => {
const {
register,
Expand Down
9 changes: 4 additions & 5 deletions apps/web/app/(app)/automation/RulesPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const examplePrompts = [
'Label pitch decks as "Pitch Deck" and forward them to [email protected]',
"Reply to cold emails by telling them to check out Inbox Zero. Then mark them as spam",
'Label high priority emails as "High Priority"',
"If a founder asks to set up a call, send them my Cal link: https://cal.com/max",
"If a founder asks to set up a call, send them my Cal link: https://cal.com/example",
"If someone asks to cancel a plan, ask to set up a call by sending my Cal link",
'If a founder sends me an investor update, label it "Investor Update" and archive it',
'If someone pitches me their startup, label it as "Investing", archive it, and respond with a friendly reply that I no longer have time to look at the email but if they get a warm intro, that\'s their best bet to get funding from me',
Expand Down Expand Up @@ -151,7 +151,7 @@ function RulesPromptForm({
<div className="sm:col-span-2">
<CardHeader>
<CardTitle>
How your AI personal assistant should handle your emails
How your AI personal assistant should handle incoming emails
</CardTitle>
<CardDescription>
Write a prompt for your assistant to follow.
Expand All @@ -176,9 +176,8 @@ Feel free to add as many as you want:
* Archive all marketing emails.
* Label receipts as "Receipt" and forward them to [email protected].
* Label emails that require a reply as "Reply Required".
* If a customer asks to set up a call, send them my Cal link: https://cal.com/max
* Review any emails from [email protected] and see if any are about finance. If so, respond with a friendly draft a reply that answers the question.
`}
* If a customer asks to set up a call, send them my Cal link: https://cal.com/example
* Review any emails from [email protected] and see if any are about finance. If so, respond with a friendly draft a reply that answers the question.`}
/>

<div className="flex gap-2">
Expand Down
26 changes: 12 additions & 14 deletions apps/web/app/(app)/bulk-unsubscribe/BulkUnsubscribeMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ export function BulkUnsubscribeRowMobile({
mutate,
posthog,
});
const { unsubscribeLoading, onUnsubscribe } = useUnsubscribe({
item,
hasUnsubscribeAccess,
mutate,
posthog,
refetchPremium,
});
const { unsubscribeLoading, onUnsubscribe, unsubscribeLink } = useUnsubscribe(
{
item,
hasUnsubscribeAccess,
mutate,
refetchPremium,
posthog,
},
);
const { archiveAllLoading, onArchiveAll } = useArchiveAll({
item,
posthog,
Expand Down Expand Up @@ -117,15 +119,11 @@ export function BulkUnsubscribeRowMobile({
variant={
item.status === NewsletterStatus.UNSUBSCRIBED ? "red" : "default"
}
asChild={!!item.lastUnsubscribeLink}
asChild
>
<Link
href={
hasUnsubscribeAccess && item.lastUnsubscribeLink
? cleanUnsubscribeLink(item.lastUnsubscribeLink) || "#"
: "#"
}
target="_blank"
href={unsubscribeLink}
target={unsubscribeLink !== "#" ? "_blank" : undefined}
onClick={onUnsubscribe}
rel="noreferrer"
>
Expand Down
28 changes: 12 additions & 16 deletions apps/web/app/(app)/bulk-unsubscribe/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,18 @@ function UnsubscribeButton<T extends Row>({
item: T;
hasUnsubscribeAccess: boolean;
mutate: () => Promise<void>;
posthog: PostHog;
refetchPremium: () => Promise<any>;
posthog: PostHog;
}) {
const { unsubscribeLoading, onUnsubscribe } = useUnsubscribe({
item,
hasUnsubscribeAccess,
mutate,
posthog,
refetchPremium,
});

const isLink = hasUnsubscribeAccess && item.lastUnsubscribeLink;
const { unsubscribeLoading, onUnsubscribe, unsubscribeLink } = useUnsubscribe(
{
item,
hasUnsubscribeAccess,
mutate,
posthog,
refetchPremium,
},
);

return (
<Button
Expand All @@ -179,12 +179,8 @@ function UnsubscribeButton<T extends Row>({
asChild
>
<Link
href={
isLink
? cleanUnsubscribeLink(item.lastUnsubscribeLink || "") || ""
: ""
}
target={isLink ? "_blank" : undefined}
href={unsubscribeLink}
target={unsubscribeLink !== "#" ? "_blank" : undefined}
onClick={onUnsubscribe}
rel="noreferrer"
>
Expand Down
4 changes: 4 additions & 0 deletions apps/web/app/(app)/bulk-unsubscribe/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export function useUnsubscribe<T extends Row>({
return {
unsubscribeLoading,
onUnsubscribe,
unsubscribeLink:
hasUnsubscribeAccess && item.lastUnsubscribeLink
? cleanUnsubscribeLink(item.lastUnsubscribeLink) || "#"
: "#",
elie222 marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { PenIcon } from "lucide-react";
import { Modal, useModal } from "@/components/Modal";
import { Button } from "@/components/Button";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/Input";
import {
type UpdateColdEmailSettingsBody,
Expand All @@ -29,7 +29,7 @@ export function ColdEmailPromptModal(props: {

return (
<>
<Button onClick={openModal} type="button" color="white">
<Button onClick={openModal} type="button" variant="outline">
<PenIcon className="mr-2 h-4 w-4" />
Edit Prompt
</Button>
Expand Down Expand Up @@ -102,7 +102,7 @@ function ColdEmailPromptForm(props: {
/>

<div className="mt-2">
<Button type="submit" size="lg" loading={isSubmitting}>
<Button type="submit" loading={isSubmitting}>
Save
</Button>
</div>
Expand Down
64 changes: 43 additions & 21 deletions apps/web/app/(app)/cold-email-blocker/ColdEmailSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useCallback, useMemo } from "react";
import { type SubmitHandler, useForm } from "react-hook-form";
import { Controller, type SubmitHandler, useForm } from "react-hook-form";
import useSWR from "swr";
import type { UserResponse } from "@/app/api/user/me/route";
import type { SaveEmailUpdateSettingsResponse } from "@/app/api/user/settings/email-updates/route";
Expand All @@ -10,15 +10,15 @@ import { toastError, toastSuccess } from "@/components/Toast";
import { postRequest } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { ColdEmailSetting } from "@prisma/client";
import { Select } from "@/components/Select";
import { isError } from "@/utils/error";
import { Button } from "@/components/Button";
import { Button } from "@/components/ui/button";
import {
type UpdateColdEmailSettingsBody,
updateColdEmailSettingsBody,
} from "@/app/api/user/settings/cold-email/validation";
import { TestRules } from "@/app/(app)/cold-email-blocker/TestRules";
import { ColdEmailPromptModal } from "@/app/(app)/cold-email-blocker/ColdEmailPromptModal";
import { RadioGroup } from "@/components/RadioGroup";

export function ColdEmailSettings() {
const { data, isLoading, error, mutate } =
Expand All @@ -29,7 +29,7 @@ export function ColdEmailSettings() {
{data && (
<>
<ColdEmailForm coldEmailBlocker={data.coldEmailBlocker} />
<div className="mt-2 space-x-2">
<div className="mt-2 flex items-center gap-2">
<TestRules />
<ColdEmailPromptModal
coldEmailPrompt={data.coldEmailPrompt}
Expand All @@ -42,14 +42,22 @@ export function ColdEmailSettings() {
);
}

function ColdEmailForm(props: { coldEmailBlocker?: ColdEmailSetting | null }) {
export function ColdEmailForm({
coldEmailBlocker,
onSuccess,
}: {
coldEmailBlocker?: ColdEmailSetting | null;
onSuccess?: () => void;
}) {
const {
register,
control,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<UpdateColdEmailSettingsBody>({
resolver: zodResolver(updateColdEmailSettingsBody),
defaultValues: { coldEmailBlocker: props.coldEmailBlocker },
defaultValues: {
coldEmailBlocker: coldEmailBlocker || ColdEmailSetting.DISABLED,
},
});

const onSubmit: SubmitHandler<UpdateColdEmailSettingsBody> = useCallback(
Expand All @@ -67,46 +75,60 @@ function ColdEmailForm(props: { coldEmailBlocker?: ColdEmailSetting | null }) {
});
} else {
toastSuccess({ description: "Settings updated!" });
onSuccess?.();
}
},
[],
);
elie222 marked this conversation as resolved.
Show resolved Hide resolved

const options: { label: string; value: ColdEmailSetting }[] = useMemo(
const onSubmitForm = handleSubmit(onSubmit);

const options: {
value: ColdEmailSetting;
label: string;
description: string;
}[] = useMemo(
() => [
{
label: 'Archive and label as "Cold Email"',
value: ColdEmailSetting.ARCHIVE_AND_LABEL,
label: "Archive & Label",
description: "Archive and label cold emails",
},
{
label: 'Label as "Cold Email"',
value: ColdEmailSetting.LABEL,
label: "Label Only",
description: "Label cold emails as 'Cold Email', but keep in inbox",
},
{
label: "Only list here",
value: ColdEmailSetting.LIST,
label: "List in App",
description: "List cold emails in app. Make no changes to inbox.",
},
{
label: "Disabled",
value: ColdEmailSetting.DISABLED,
label: "Turn Off",
description: "Disable cold email detection",
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

Unnecessary use of useMemo for static options

The options array is wrapped in useMemo without any dependencies, which is unnecessary because the options are static and do not change between renders. Removing useMemo can simplify the code without affecting performance.

Apply this diff to remove useMemo:

   }[] = useMemo(
-    () => [
+    [
       {
         value: ColdEmailSetting.ARCHIVE_AND_LABEL,
         label: "Archive & Label",
         description: "Archive and label cold emails",
       },
       {
         value: ColdEmailSetting.LABEL,
         label: "Label Only",
         description: "Label cold emails as 'Cold Email', but keep in inbox",
       },
       {
         value: ColdEmailSetting.LIST,
         label: "List in App",
         description: "List cold emails in app. Make no changes to inbox.",
       },
       {
         value: ColdEmailSetting.DISABLED,
         label: "Turn Off",
         description: "Disable cold email detection",
       },
-    ],
-    [],
-  );
+    ];
📝 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
const options: {
value: ColdEmailSetting;
label: string;
description: string;
}[] = useMemo(
() => [
{
label: 'Archive and label as "Cold Email"',
value: ColdEmailSetting.ARCHIVE_AND_LABEL,
label: "Archive & Label",
description: "Archive and label cold emails",
},
{
label: 'Label as "Cold Email"',
value: ColdEmailSetting.LABEL,
label: "Label Only",
description: "Label cold emails as 'Cold Email', but keep in inbox",
},
{
label: "Only list here",
value: ColdEmailSetting.LIST,
label: "List in App",
description: "List cold emails in app. Make no changes to inbox.",
},
{
label: "Disabled",
value: ColdEmailSetting.DISABLED,
label: "Turn Off",
description: "Disable cold email detection",
const options: {
value: ColdEmailSetting;
label: string;
description: string;
}[] = [
{
value: ColdEmailSetting.ARCHIVE_AND_LABEL,
label: "Archive & Label",
description: "Archive and label cold emails",
},
{
value: ColdEmailSetting.LABEL,
label: "Label Only",
description: "Label cold emails as 'Cold Email', but keep in inbox",
},
{
value: ColdEmailSetting.LIST,
label: "List in App",
description: "List cold emails in app. Make no changes to inbox.",
},
{
value: ColdEmailSetting.DISABLED,
label: "Turn Off",
description: "Disable cold email detection",
},
];

},
],
[],
);

const onSubmitForm = handleSubmit(onSubmit);

return (
<form onSubmit={onSubmitForm} className="flex max-w-sm items-end space-x-2">
<Select
<form onSubmit={onSubmitForm} className="max-w-lg">
<Controller
name="coldEmailBlocker"
label="How should we handle cold emails?"
options={options}
registerProps={register("coldEmailBlocker")}
error={errors.coldEmailBlocker}
control={control}
render={({ field }) => (
<RadioGroup
label="How should we handle cold emails?"
options={options}
{...field}
error={errors.coldEmailBlocker}
/>
)}
/>

<div className="">
<div className="mt-2">
<Button type="submit" loading={isSubmitting}>
Save
</Button>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(app)/cold-email-blocker/TestRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { type SubmitHandler, useForm } from "react-hook-form";
import useSWR from "swr";
import { BookOpenCheckIcon, SparklesIcon } from "lucide-react";
import { useSession } from "next-auth/react";
import { Button } from "@/components/Button";
import { Button } from "@/components/ui/button";
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Inconsistent Button import paths detected.

Multiple files are still importing Button from the old path @/components/Button. Please update these import statements to @/components/ui/button to maintain consistency across the codebase.

Affected files:

  • apps/web/components/TopNav.tsx
  • apps/web/components/PremiumAlert.tsx
  • apps/web/app/(landing)/error.tsx
  • apps/web/app/(landing)/home/CTAButtons.tsx
  • apps/web/app/(landing)/login/LoginForm.tsx
  • apps/web/app/(landing)/login/error/page.tsx
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(landing)/thank-you/page.tsx
  • apps/web/components/ListHeading.tsx
  • apps/web/components/GroupHeading.tsx
  • apps/web/components/ErrorPage.tsx
  • apps/web/app/(app)/bulk-archive/page.tsx
  • apps/web/app/(app)/license/page.tsx
  • apps/web/app/(app)/settings/LabelsSection.tsx
  • apps/web/app/(app)/settings/MultiAccountSection.tsx
  • apps/web/app/(app)/settings/ModelSection.tsx
  • apps/web/app/(app)/settings/EmailUpdatesSection.tsx
  • apps/web/app/(app)/stats/StatsOnboarding.tsx
  • apps/web/app/(app)/onboarding-old2/page.tsx
  • apps/web/app/(app)/settings/DeleteSection.tsx
  • apps/web/app/(app)/simple/SimpleList.tsx
  • apps/web/app/(app)/settings/ApiKeysCreateForm.tsx
  • apps/web/app/(app)/settings/AboutSectionForm.tsx
  • apps/web/app/(app)/request-access/page.tsx
  • apps/web/app/(app)/premium/Pricing.tsx
  • apps/web/app/(app)/automation/TestRules.tsx
  • apps/web/app/(app)/admin/AdminUpgradeUserForm.tsx
  • apps/web/components/Celebration.tsx
🔗 Analysis chain

Approve import path change and suggest consistency check.

The updated import path for the Button component reflects an improved organization of UI components. This change is beneficial for maintainability and clarity.

To ensure consistency across the codebase, please run the following script:

This will help identify any files that may still be using the old import path and confirm the usage of the new path.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining old Button import statements

# Test: Search for old Button import statements
rg --type typescript --type tsx 'from\s+["'\'']@/components/Button["'\'']'

# Test: Verify new Button import statements
rg --type typescript --type tsx 'from\s+["'\'']@/components/ui/button["'\'']'

Length of output: 219


Script:

#!/bin/bash
# Description: Check for any remaining old Button import statements by targeting .ts and .tsx files

# Search for old Button import statements
rg -g "*.ts" -g "*.tsx" 'from\s+["'"'"']@/components/Button["'"'"']'

# Search for new Button import statements
rg -g "*.ts" -g "*.tsx" 'from\s+["'"'"']@/components/ui/button["'"'"']'

Length of output: 9116

import { Input } from "@/components/Input";
import { toastError } from "@/components/Toast";
import { postRequest } from "@/utils/api";
Expand All @@ -32,7 +32,7 @@ export function TestRules() {
description="Test which emails are flagged as cold emails. We also check if the sender has emailed you before and if it includes unsubscribe links."
content={<TestRulesContent />}
>
<Button color="white" type="button">
<Button type="button" variant="outline">
<BookOpenCheckIcon className="mr-2 h-4 w-4" />
Test
</Button>
Expand Down
Loading