Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d8a4b91
add stripe package
elie222 May 4, 2025
988afe9
Merge branch 'main' into stripe
elie222 May 4, 2025
5e81536
add stripe mvp
elie222 May 5, 2025
64d0b45
add ee license
elie222 May 5, 2025
c5ea1ee
stripe tier
elie222 May 5, 2025
9051a9c
Merge branch 'main' into stripe
elie222 May 5, 2025
086c545
clean up feature access
elie222 May 5, 2025
bebc6fa
stripe is premium
elie222 May 5, 2025
a3ed600
save subscription item id
elie222 May 5, 2025
b20a4c4
fix is premium stripe
elie222 May 5, 2025
570609a
Add stripe billing portal
elie222 May 5, 2025
daedaa6
adjust button copy
elie222 May 5, 2025
0e3af8f
disable ph pricing tests
elie222 May 5, 2025
c51e2fe
stripe checkout links
elie222 May 6, 2025
74a39c6
price config
elie222 May 6, 2025
407b314
stripe config
elie222 May 6, 2025
3490890
turbo
elie222 May 6, 2025
85bfe72
migrate
elie222 May 6, 2025
cced41a
adjust pricing plans
elie222 May 6, 2025
640d9d8
adjust plans
elie222 May 6, 2025
2b16395
rename stripe routes
elie222 May 6, 2025
71556b1
kb business
elie222 May 6, 2025
74adfc3
fix stripe
elie222 May 6, 2025
a6d06ce
fix stripe bugs
elie222 May 6, 2025
607db25
fix copy
elie222 May 8, 2025
e1dea54
switch tier stripe
elie222 May 8, 2025
b325cdf
stripe spinner
elie222 May 8, 2025
994f022
dont pass quantity to switch plan
elie222 May 8, 2025
cbe2501
trial and quantity fix
elie222 May 8, 2025
6f9129c
check expired
elie222 May 8, 2025
da9c930
fix build
elie222 May 8, 2025
44f0a43
delete old code
elie222 May 8, 2025
eff94eb
remove as
elie222 May 8, 2025
6add7f8
fixes
elie222 May 8, 2025
e2339cd
fix unsub access
elie222 May 8, 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
25 changes: 0 additions & 25 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -82,28 +82,3 @@ NEXT_PUBLIC_CRISP_WEBSITE_ID=
# Sanity config for blog
NEXT_PUBLIC_SANITY_PROJECT_ID=
NEXT_PUBLIC_SANITY_DATASET="production"

# Payments
LEMON_SQUEEZY_SIGNING_SECRET=
LEMON_SQUEEZY_API_KEY=

NEXT_PUBLIC_BASIC_MONTHLY_PAYMENT_LINK=#
NEXT_PUBLIC_BASIC_ANNUALLY_PAYMENT_LINK=#

NEXT_PUBLIC_PRO_MONTHLY_PAYMENT_LINK=#
NEXT_PUBLIC_PRO_ANNUALLY_PAYMENT_LINK=#
NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID=123
NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID=123

NEXT_PUBLIC_BUSINESS_MONTHLY_PAYMENT_LINK=#
NEXT_PUBLIC_BUSINESS_ANNUALLY_PAYMENT_LINK=#
NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID=123
NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID=123

NEXT_PUBLIC_COPILOT_MONTHLY_PAYMENT_LINK=#
NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID=123

NEXT_PUBLIC_LIFETIME_PAYMENT_LINK=#
NEXT_PUBLIC_LIFETIME_VARIANT_ID=123
NEXT_PUBLIC_LIFETIME_EXTRA_SEATS_PAYMENT_LINK=#
NEXT_PUBLIC_LIFETIME_EXTRA_SEATS_VARIANT_ID=123
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import {
import { deleteKnowledgeAction } from "@/utils/actions/knowledge";
import { toastError, toastSuccess } from "@/components/Toast";
import { LoadingContent } from "@/components/LoadingContent";
import type { GetKnowledgeResponse } from "@/app/api/knowledge/route";
import { formatDateSimple } from "@/utils/date";
import type { Knowledge } from "@prisma/client";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { KnowledgeForm } from "@/app/(app)/[emailAccountId]/automation/knowledge/KnowledgeForm";
import { useAccount } from "@/providers/EmailAccountProvider";
import type { GetKnowledgeResponse } from "@/app/api/knowledge/route";
import type { Knowledge } from "@prisma/client";

export function KnowledgeBase() {
const { emailAccountId } = useAccount();
Expand Down Expand Up @@ -67,6 +67,7 @@ export function KnowledgeBase() {
closeDialog={handleClose}
refetch={mutate}
editingItem={editingItem}
knowledgeItemsCount={data?.items.length || 0}
/>
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { useRef } from "react";
import type { KeyedMutator } from "swr";
import { CrownIcon } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/Input";
import { useForm, Controller } from "react-hook-form";
Expand All @@ -18,22 +20,34 @@ import {
} from "@/utils/actions/knowledge";
import { toastError, toastSuccess } from "@/components/Toast";
import type { GetKnowledgeResponse } from "@/app/api/knowledge/route";
import type { Knowledge } from "@prisma/client";
import { PremiumTier, type Knowledge } from "@prisma/client";
import { Tiptap, type TiptapHandle } from "@/components/editor/Tiptap";
import { Label } from "@/components/ui/label";
import { cn } from "@/utils";
import { useAccount } from "@/providers/EmailAccountProvider";
import { usePremium } from "@/components/PremiumAlert";
import { hasTierAccess } from "@/utils/premium";
import { AlertWithButton } from "@/components/Alert";
import { KNOWLEDGE_BASIC_MAX_ITEMS } from "@/utils/config";

export function KnowledgeForm({
closeDialog,
refetch,
editingItem,
knowledgeItemsCount,
}: {
closeDialog: () => void;
refetch: KeyedMutator<GetKnowledgeResponse>;
editingItem: Knowledge | null;
knowledgeItemsCount: number;
}) {
const { emailAccountId } = useAccount();
const { tier } = usePremium();

const hasFullAccess = hasTierAccess({
tier: tier || null,
minimumTier: PremiumTier.BUSINESS_PLUS_MONTHLY,
});

const {
register,
Expand Down Expand Up @@ -91,6 +105,26 @@ export function KnowledgeForm({

return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{!editingItem &&
!hasFullAccess &&
knowledgeItemsCount >= KNOWLEDGE_BASIC_MAX_ITEMS && (
<AlertWithButton
title="Upgrade to add more knowledge base entries"
description={
<>
Switch to the Business plan to add more knowledge base entries.
</>
}
icon={<CrownIcon className="h-4 w-4" />}
button={
<Button asChild>
<Link href="/premium">Upgrade</Link>
</Button>
}
variant="blue"
/>
)}

<Input
type="text"
name="title"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import { useCallback } from "react";
import Link from "next/link";
import { type SubmitHandler, useFieldArray, useForm } from "react-hook-form";
import { useSession } from "next-auth/react";
import { zodResolver } from "@hookform/resolvers/zod";
Expand All @@ -26,7 +25,6 @@ import { AlertBasic, AlertWithButton } from "@/components/Alert";
import { usePremium } from "@/components/PremiumAlert";
import { pricingAdditonalEmail } from "@/app/(app)/premium/config";
import { PremiumTier } from "@prisma/client";
import { env } from "@/env";
import { getUserTier, isAdminForPremium } from "@/utils/premium";
import { usePremiumModal } from "@/app/(app)/premium/PremiumModal";
import { useAction } from "next-safe-action/hooks";
Expand Down Expand Up @@ -212,20 +210,9 @@ function MultiAccountForm({
})}
</div>

{needsToPurchaseMoreSeats ? (
<Button type="button" loading={isExecuting} asChild>
<Link
href={`${env.NEXT_PUBLIC_LIFETIME_EXTRA_SEATS_PAYMENT_LINK}?quantity=${extraSeats}`}
target="_blank"
>
Purchase {extraSeats} Extra Seat{extraSeats > 1 ? "s" : ""}
</Link>
</Button>
) : (
<Button type="submit" loading={isExecuting}>
Save
</Button>
)}
<Button type="submit" loading={isExecuting}>
Save
</Button>
Comment on lines +213 to +215
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Simplified UI for Stripe integration.

Removed the conditional Lemon Squeezy payment button in favor of a simple "Save" button, aligning with the migration to Stripe-based billing.

Note that the form submission still blocks if extra seats are needed (line 176), but no longer provides a direct purchase option. Ensure this doesn't confuse users who need to upgrade but aren't given a clear path to do so.


🏁 Script executed:

#!/bin/bash
# Check if there's another mechanism for users to upgrade when they need more seats
# Looking for upgrade modals or navigation in the codebase

# Search for places that might handle the upgrade flow for users needing more seats
rg -A 3 "needsToPurchaseMoreSeats" --type=tsx --type=ts

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Locate the MultiAccountSection component and inspect the block around line 176
file=$(fd -e tsx MultiAccountSection.tsx)
echo "Inspecting: $file"
echo "----- Lines 160–200 -----"
sed -n '160,200p' "$file"

echo
echo "----- Searching for upgrade, billing, or seat keywords in this file -----"
rg -n -C3 'upgrade|billing|seat' "$file"

Length of output: 2727


Provide a clear upgrade path when seat limit is reached

The “Save” button now replaces the old Lemon Squeezy purchase flow for Stripe migration. We verified that:

  • In the onSubmit callback (apps/web/app/(app)/[emailAccountId]/settings/MultiAccountSection.tsx lines 168–174), submission simply returns if needsToPurchaseMoreSeats is true.
  • The ExtraSeatsAlert component (same file, lines 220–228) only renders a static alert showing current seats without offering any action.

As a result, users who hit the seat limit have no obvious way to upgrade. Please add an explicit upgrade option, for example:

• In the onSubmit block, redirect to your billing page or open an “Upgrade plan” modal when needsToPurchaseMoreSeats is true.
• Enhance ExtraSeatsAlert to include an “Upgrade plan” button or link alongside the seat information.

Comment on lines +213 to +215
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

UI simplified for the Stripe integration.

The UI has been streamlined by removing the Lemon Squeezy payment link button and keeping only the Save button. This aligns with the migration to Stripe for subscription management.

However, there's no clear alternative action for users who need to purchase more seats. Consider adding guidance for users when needsToPurchaseMoreSeats is true:

-      <Button type="submit" loading={isExecuting}>
-        Save
-      </Button>
+      {needsToPurchaseMoreSeats ? (
+        <>
+          <Button type="submit" loading={isExecuting} disabled>
+            Save
+          </Button>
+          <p className="mt-2 text-sm text-muted-foreground">
+            You need to purchase additional seats before saving. Visit the billing section to upgrade.
+          </p>
+        </>
+      ) : (
+        <Button type="submit" loading={isExecuting}>
+          Save
+        </Button>
+      )}
📝 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
<Button type="submit" loading={isExecuting}>
Save
</Button>
{needsToPurchaseMoreSeats ? (
<>
<Button type="submit" loading={isExecuting} disabled>
Save
</Button>
<p className="mt-2 text-sm text-muted-foreground">
You need to purchase additional seats before saving. Visit the billing section to upgrade.
</p>
</>
) : (
<Button type="submit" loading={isExecuting}>
Save
</Button>
)}

</form>
);
}
Expand Down
5 changes: 4 additions & 1 deletion apps/web/app/(app)/[emailAccountId]/usage/usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export function Usage(props: {
stats={[
{
name: "Unsubscribe Credits",
value: isPremium(premium?.lemonSqueezyRenewsAt || null)
value: isPremium(
premium?.lemonSqueezyRenewsAt || null,
premium?.stripeSubscriptionStatus || null,
)
? "Unlimited"
: formatStat(
premium?.unsubscribeCredits ??
Expand Down
16 changes: 10 additions & 6 deletions apps/web/app/(app)/admin/AdminUpgradeUserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useAction } from "next-safe-action/hooks";
import { type SubmitHandler, useForm } from "react-hook-form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/Input";
import { changePremiumStatusAction } from "@/utils/actions/premium";
import { adminChangePremiumStatusAction } from "@/utils/actions/premium";
import { Select } from "@/components/Select";
import {
changePremiumStatusSchema,
Expand All @@ -17,7 +17,7 @@ import { toastError, toastSuccess } from "@/components/Toast";

export const AdminUpgradeUserForm = () => {
const { execute: changePremiumStatus, isExecuting } = useAction(
changePremiumStatusAction,
adminChangePremiumStatusAction,
{
onSuccess: () => {
toastSuccess({
Expand Down Expand Up @@ -91,6 +91,14 @@ export const AdminUpgradeUserForm = () => {
label: PremiumTier.BUSINESS_MONTHLY,
value: PremiumTier.BUSINESS_MONTHLY,
},
{
label: PremiumTier.BUSINESS_PLUS_ANNUALLY,
value: PremiumTier.BUSINESS_PLUS_ANNUALLY,
},
{
label: PremiumTier.BUSINESS_PLUS_MONTHLY,
value: PremiumTier.BUSINESS_PLUS_MONTHLY,
},
{
label: PremiumTier.PRO_ANNUALLY,
value: PremiumTier.PRO_ANNUALLY,
Expand All @@ -111,10 +119,6 @@ export const AdminUpgradeUserForm = () => {
label: PremiumTier.COPILOT_MONTHLY,
value: PremiumTier.COPILOT_MONTHLY,
},
{
label: PremiumTier.LIFETIME,
value: PremiumTier.LIFETIME,
},
]}
{...register("period")}
error={errors.period}
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(app)/admin/validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ export const changePremiumStatusSchema = z.object({
PremiumTier.PRO_ANNUALLY,
PremiumTier.BUSINESS_MONTHLY,
PremiumTier.BUSINESS_ANNUALLY,
PremiumTier.BUSINESS_PLUS_MONTHLY,
PremiumTier.BUSINESS_PLUS_ANNUALLY,
PremiumTier.COPILOT_MONTHLY,
PremiumTier.LIFETIME,
]),
count: z.coerce.number().optional(),
upgrade: z.boolean(),
Expand Down
Loading