Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -10,7 +10,7 @@ export function AIChatButton() {
return (
<Button
size="sm"
variant="primaryBlue"
variant="outline"
onClick={() => setOpen(["chat-sidebar"])}
>
<MessageCircleIcon className="mr-2 size-4" />
Expand Down
43 changes: 19 additions & 24 deletions apps/web/app/(app)/[emailAccountId]/automation/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { Suspense } from "react";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { PlayIcon } from "lucide-react";
import prisma from "@/utils/prisma";
import { History } from "@/app/(app)/[emailAccountId]/assistant/History";
import { Pending } from "@/app/(app)/[emailAccountId]/assistant/Pending";
import { Tabs, TabsContent } from "@/components/ui/tabs";
import { Process } from "@/app/(app)/[emailAccountId]/assistant/Process";
import { OnboardingModal } from "@/components/OnboardingModal";
import { OnboardingDialogContent } from "@/components/OnboardingModal";
import { PermissionsCheck } from "@/app/(app)/[emailAccountId]/PermissionsCheck";
import { EmailProvider } from "@/providers/EmailProvider";
import { ASSISTANT_ONBOARDING_COOKIE } from "@/utils/cookies";
import { prefixPath } from "@/utils/path";
import { PremiumAlertWithData } from "@/components/PremiumAlert";
import { checkUserOwnsEmailAccount } from "@/utils/email-account";
import { SettingsTab } from "@/app/(app)/[emailAccountId]/assistant/settings/SettingsTab";
import { PageHeading } from "@/components/Typography";
import { TabSelect } from "@/components/TabSelect";
import { RulesTab } from "@/app/(app)/[emailAccountId]/assistant/RulesTab";
import { AIChatButton } from "@/app/(app)/[emailAccountId]/assistant/AIChatButton";
import { PageWrapper } from "@/components/PageWrapper";
import { PageHeader } from "@/components/PageHeader";
import { Button } from "@/components/ui/button";
import { Dialog, DialogTrigger } from "@/components/ui/dialog";

export const maxDuration = 300; // Applies to the actions

Expand Down Expand Up @@ -88,8 +91,20 @@ export default async function AutomationPage({
<PremiumAlertWithData className="mb-2" />

<div className="flex items-center justify-between">
<PageHeading>Assistant</PageHeading>
<ExtraActions />
<div>
<PageHeader
title="AI Assistant"
description="Personalized AI to help you manage emails faster."
video={{
title: "Getting started with AI Personal Assistant",
description:
"Learn how to use the AI Personal Assistant to automatically label, archive, and more.",
videoId: "SoeNDVr7ve4",
}}
/>
</div>

<AIChatButton />
</div>

<div className="border-b border-neutral-200 pt-2">
Expand Down Expand Up @@ -175,23 +190,3 @@ async function PendingTab({
</TabsContent>
);
}

function ExtraActions() {
return (
<div className="flex items-center gap-2">
<OnboardingModal
title="Getting started with AI Personal Assistant"
description={
<>
Learn how to use the AI Personal Assistant to automatically label,
archive, and more.
</>
}
videoId="SoeNDVr7ve4"
buttonProps={{ size: "sm", variant: "ghost" }}
/>

<AIChatButton />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ import { ArchiveProgress } from "@/app/(app)/[emailAccountId]/bulk-unsubscribe/A
import { ClientOnly } from "@/components/ClientOnly";
import { Toggle } from "@/components/Toggle";
import { useAccount } from "@/providers/EmailAccountProvider";
import { PageHeading } from "@/components/Typography";
import { useWindowSize } from "usehooks-ts";
import { ActionBar } from "@/app/(app)/[emailAccountId]/stats/ActionBar";
import { LoadStatsButton } from "@/app/(app)/[emailAccountId]/stats/LoadStatsButton";
import { PageWrapper } from "@/components/PageWrapper";
import { PageHeader } from "@/components/PageHeader";
import { TextLink } from "@/components/Typography";

const selectOptions = [
{ label: "Last week", value: "7" },
Expand Down Expand Up @@ -208,7 +209,28 @@ export function BulkUnsubscribe() {

return (
<PageWrapper>
<PageHeading>Bulk Unsubscriber</PageHeading>
<PageHeader
title="Bulk Unsubscriber"
description="Unsubscribe from and archive emails you don't want to receive."
video={{
title: "Getting started with Bulk Unsubscribe",
description: (
<>
Learn how to quickly bulk unsubscribe from and archive unwanted
emails. You can read more in our{" "}
<TextLink
href="https://docs.getinboxzero.com/essentials/bulk-email-unsubscriber"
target="_blank"
rel="noopener noreferrer"
>
help center
</TextLink>
.
</>
),
videoId: "T1rnooV4OYc",
}}
/>

<div className="items-center justify-between flex mt-4 flex-wrap">
<div className="flex items-center justify-end gap-1">
Expand Down
59 changes: 39 additions & 20 deletions apps/web/components/OnboardingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,34 +57,53 @@ export function OnboardingModalDialog({
title: string;
description: React.ReactNode;
videoId: string;
}) {
return (
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<OnboardingDialogContent
title={title}
description={description}
videoId={videoId}
/>
</Dialog>
);
}

export function OnboardingDialogContent({
title,
description,
videoId,
}: {
title: string;
description: React.ReactNode;
videoId: string;
}) {
const { width } = useWindowSize();

const videoWidth = Math.min(width * 0.75, 1200);
const videoHeight = videoWidth * (675 / 1200);

return (
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<DialogContent className="min-w-[350px] sm:min-w-[600px] md:min-w-[750px] lg:min-w-[880px] xl:min-w-[1280px]">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogContent className="min-w-[350px] sm:min-w-[600px] md:min-w-[750px] lg:min-w-[880px] xl:min-w-[1280px]">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>

<YouTubeVideo
videoId={videoId}
iframeClassName="mx-auto"
opts={{
height: `${videoHeight}`,
width: `${videoWidth}`,
playerVars: {
// https://developers.google.com/youtube/player_parameters
autoplay: 1,
},
}}
/>
</DialogContent>
</Dialog>
<YouTubeVideo
videoId={videoId}
title={`Onboarding video - ${title}`}
iframeClassName="mx-auto"
opts={{
height: `${videoHeight}`,
width: `${videoWidth}`,
playerVars: {
// https://developers.google.com/youtube/player_parameters
autoplay: 1,
},
}}
/>
</DialogContent>
);
}

Expand Down
49 changes: 49 additions & 0 deletions apps/web/components/PageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { OnboardingDialogContent } from "@/components/OnboardingModal";
import { PageHeading, PageSubHeading } from "@/components/Typography";
import { Button } from "@/components/ui/button";
import { Dialog, DialogTrigger } from "@/components/ui/dialog";
import { PlayIcon } from "lucide-react";

type Video = {
title: string;
description: React.ReactNode;
videoId: string;
};

export function PageHeader({
title,
description,
video,
}: {
title: string;
description: string;
video?: Video;
}) {
return (
<div>
<PageHeading>{title}</PageHeading>
<div className="flex items-center mt-1">
<PageSubHeading>{description}</PageSubHeading>
{video && <WatchVideo video={video} />}
</div>
</div>
);
}

function WatchVideo({ video }: { video: Video }) {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="xs" className="ml-3">
<PlayIcon className="mr-2 size-3" />
Watch Video
</Button>
</DialogTrigger>
<OnboardingDialogContent
title={video.title}
description={video.description}
videoId={video.videoId}
/>
</Dialog>
);
}
13 changes: 13 additions & 0 deletions apps/web/components/Typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ const PageHeading = forwardRef<
));
PageHeading.displayName = "PageHeading";

const PageSubHeading = forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
PageSubHeading.displayName = "PageSubHeading";

const SectionHeader = forwardRef<
HTMLHeadingElement,
React.HTMLAttributes<HTMLHeadingElement>
Expand Down Expand Up @@ -108,6 +120,7 @@ TextLink.displayName = "TextLink";

export {
PageHeading,
PageSubHeading,
SectionHeader,
TypographyH3,
TypographyH4,
Expand Down
2 changes: 2 additions & 0 deletions apps/web/components/YouTubeVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { cn } from "@/utils";

export function YouTubeVideo(props: {
videoId: string;
title?: string;
iframeClassName?: string;
className?: string;
opts?: {
Expand All @@ -16,6 +17,7 @@ export function YouTubeVideo(props: {
return (
<YouTube
videoId={props.videoId}
title={props.title}
className={cn("aspect-video h-full w-full rounded-lg", props.className)}
iframeClassName={props.iframeClassName}
opts={{
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.5.47
v2.5.48
Loading