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
10 changes: 8 additions & 2 deletions apps/web/app/(app)/premium/Pricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { usePricingVariant } from "@/hooks/useFeatureFlags";
import { PremiumTier } from "@prisma/client";
import { switchPremiumPlanAction } from "@/utils/actions/premium";
import { isActionError } from "@/utils/error";
import { TooltipExplanation } from "@/components/TooltipExplanation";

function attachUserInfo(
url: string,
Expand Down Expand Up @@ -253,12 +254,17 @@ export function Pricing(props: { header?: React.ReactNode }) {
)}
<ul className="mt-8 space-y-3 text-sm leading-6 text-gray-600">
{tier.features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<li key={feature.text} className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600"
aria-hidden="true"
/>
{feature}
<span className="flex items-center gap-2">
{feature.text}
{feature.tooltip && (
<TooltipExplanation text={feature.tooltip} />
)}
</span>
</li>
))}
</ul>
Expand Down
87 changes: 64 additions & 23 deletions apps/web/app/(app)/premium/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { env } from "@/env";
import { PremiumTier } from "@prisma/client";

type Feature = {
text: string;
tooltip?: string;
};

type Tier = {
name: string;
tiers: { monthly: PremiumTier; annually: PremiumTier };
Expand All @@ -9,7 +14,7 @@ type Tier = {
priceAdditional: { monthly: number; annually: number };
discount: { monthly: number; annually: number };
description: string;
features: string[];
features: Feature[];
cta: string;
ctaLink?: string;
mostPopular?: boolean;
Expand Down Expand Up @@ -68,6 +73,39 @@ function discount(monthly: number, annually: number) {
return ((monthly - annually) / monthly) * 100;
}

const aiAssistantFeature = {
text: "AI personal assistant",
tooltip: "AI assistant that drafts replies and organizes your inbox.",
};

const replyZeroFeature = {
text: "Reply Zero",
tooltip:
"Never miss a reply or follow up again. Every conversation missing a reply is labeled.",
};

const coldEmailBlockerFeature = {
text: "Cold email blocker",
tooltip: "Automatically block cold emails.",
};

const smartCategoriesFeature = {
text: "Smart categories",
tooltip:
"Categorizes your emails into meaningful groups to take bulk actions on or apply rules to. e.g., archive thousands of newsletters in one click.",
};

const bulkUnsubscribeFeature = {
text: "Bulk unsubscribe",
tooltip:
"Bulk unsubscribe from emails in one-click based on who emails you most, and which ones you don't read.",
};

const analyticsFeature = {
text: "Email analytics",
tooltip: "Understand your email habits and patterns.",
};

const basicTier = {
name: "Unsubscriber",
tiers: {
Expand All @@ -89,10 +127,10 @@ const basicTier = {
},
description: "Unlimited unsubscribe credits.",
features: [
"Bulk email unsubscriber",
"Unlimited unsubscribes",
"Unlimited archives",
"Email analytics",
bulkUnsubscribeFeature,
analyticsFeature,
{ text: "Unlimited unsubscribes" },
{ text: "Unlimited archives" },
],
cta: "Try free for 7 days",
};
Expand Down Expand Up @@ -123,13 +161,15 @@ const businessTier = {
},
description: "Unlock full AI-powered email management",
features: [
"Everything in Unsubscriber tier",
"AI personal assistant",
"Reply Zero",
"Cold email blocker",
"Smart categories",
"Unlimited AI usage",
"Priority support",
{
text: "Everything in Unsubscriber tier",
},
aiAssistantFeature,
replyZeroFeature,
coldEmailBlockerFeature,
smartCategoriesFeature,
{ text: "Unlimited AI usage" },
{ text: "Priority support" },
],
cta: "Try free for 7 days",
mostPopular: true,
Expand Down Expand Up @@ -159,13 +199,14 @@ export const businessSingleTier: Tier = {
},
description: "Unlock full AI-powered email management",
features: [
"AI personal assistant",
"Cold email blocker",
"Smart categories",
"Unlimited AI credits",
"Bulk email unsubscriber",
"Email analytics",
"Priority support",
aiAssistantFeature,
replyZeroFeature,
coldEmailBlockerFeature,
smartCategoriesFeature,
{ text: "Unlimited AI usage" },
bulkUnsubscribeFeature,
analyticsFeature,
{ text: "Priority support" },
],
cta: "Try free for 7 days",
};
Expand All @@ -191,10 +232,10 @@ const copilotTier = {
discount: { monthly: 0, annually: 0 },
description: "Expert human assistant to manage your email",
features: [
"Everything in AI Assistant tier",
"Human assistant to manage your email daily",
"30-minute 1:1 monthly call",
"Full refund if not satisfied after first 3 days",
{ text: "Everything in AI Assistant tier" },
{ text: "Human assistant to manage your email daily" },
{ text: "30-minute 1:1 monthly call" },
{ text: "Full refund if not satisfied after first 3 days" },
],
cta: "Book a call",
ctaLink: env.NEXT_PUBLIC_CALL_LINK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function CreateCategoryForm({
error={errors.description}
/>

<div className="rounded border border-gray-200 bg-gray-50 p-3">
<div className="rounded border border-border bg-muted/50 p-3">
<div className="text-xs font-medium">Examples</div>
<div className="mt-1 flex flex-wrap gap-2">
{EXAMPLE_CATEGORIES.map((category) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ function CategoryCard({
<Card
className={cn(
"flex items-center justify-between gap-2 p-4",
!isEnabled && "bg-gray-200",
!isEnabled && "bg-muted/50",
)}
>
<div>
Expand Down
8 changes: 4 additions & 4 deletions apps/web/components/GroupedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,10 @@ function GroupRow({
onRemoveAllFromCategory: () => void;
}) {
return (
<TableRow className="h-8 cursor-pointer bg-gray-50">
<TableRow className="h-8 cursor-pointer bg-muted/50">
<TableCell
colSpan={3}
className="py-1 text-sm font-medium text-gray-700"
className="py-1 text-sm font-medium text-foreground"
onClick={onToggle}
>
<div className="flex items-center">
Expand All @@ -396,7 +396,7 @@ function GroupRow({
)}
/>
{category.name}
<span className="ml-2 text-xs text-gray-500">({count})</span>
<span className="ml-2 text-xs text-muted-foreground">({count})</span>
</div>
</TableCell>
<TableCell className="flex justify-end gap-1.5 py-1">
Expand Down Expand Up @@ -576,7 +576,7 @@ function ArchiveStatusCell({ sender }: { sender: string }) {
</span>
);
}
return <span className="text-gray-500">Archived</span>;
return <span className="text-muted-foreground">Archived</span>;
case "processing":
return (
<span className="text-blue-500">
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function TopBar({ children, className, sticky = false }: TopBarProps) {
return (
<div
className={cn(
"justify-between border-b bg-background px-2 py-2 shadow sm:flex sm:px-4",
"justify-between border-b border-border bg-background px-2 py-2 shadow sm:flex sm:px-4",
sticky && "top-0 z-10 sm:sticky",
className,
)}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/TopSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function TopSection(props: {
descriptionComponent?: React.ReactNode;
}) {
return (
<div className="content-container border-b border-gray-200 bg-white py-4 shadow-sm sm:py-6">
<div className="content-container border-b border-border bg-background py-4 shadow-sm sm:py-6">
<PageHeading>{props.title}</PageHeading>
<div className="mt-2">
{props.descriptionComponent ? (
Expand All @@ -28,7 +28,7 @@ export function TopSectionWithRightSection(props: {
rightComponent: React.ReactNode;
}) {
return (
<div className="content-container flex items-center justify-between border-b border-gray-200 bg-white py-6 shadow-sm">
<div className="content-container flex items-center justify-between border-b border-border bg-background py-6 shadow-sm">
<div>
<PageHeading>{props.title}</PageHeading>
<div className="mt-2">
Expand Down
Loading