Skip to content

Commit

Permalink
add pro plan validation, customers table -> app_metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
xvvvyz committed Aug 1, 2024
1 parent f6421d6 commit 79c38bf
Show file tree
Hide file tree
Showing 36 changed files with 326 additions and 144 deletions.
2 changes: 1 addition & 1 deletion app/(pages)/(auth)/change-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const metadata = { title: 'Change password' };

const Page = () => (
<div className="w-full sm:rounded sm:border sm:border-alpha-1 sm:bg-bg-2 sm:p-8">
<h1 className="mb-10 text-2xl">Change your password</h1>
<h1 className="mb-12 text-2xl">Change your password</h1>
<ChangePasswordForm />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(auth)/forgot-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Page = () => (
<>
<div className="sm:rounded sm:border sm:border-alpha-1 sm:bg-bg-2 sm:p-8">
<h1 className="text-2xl">Forgot your password?</h1>
<p className="mb-10 mt-3 text-fg-4">
<p className="mb-12 mt-3 text-fg-4">
Enter the email address associated with your account and we will send
you a link to change&nbsp;your&nbsp;password.
</p>
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface PageProps {
const Page = ({ searchParams: { next } }: PageProps) => (
<>
<div className="w-full sm:rounded sm:border sm:border-alpha-1 sm:bg-bg-2 sm:p-8">
<h1 className="mb-10 text-2xl">Welcome back</h1>
<h1 className="mb-12 text-2xl">Welcome back</h1>
<SignInForm next={next} />
</div>
<p className="flex gap-4">
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(auth)/sign-up/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface PageProps {
const Page = ({ searchParams: { next } }: PageProps) => (
<>
<div className="sm:rounded sm:border sm:border-alpha-1 sm:bg-bg-2 sm:p-8">
<h1 className="mb-10 text-2xl">Create your account</h1>
<h1 className="mb-12 text-2xl">Create your account</h1>
<SignUpForm next={next} />
</div>
<p className="flex gap-4">
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(with-nav)/inputs/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Layout = ({ children }: LayoutProps) => (
<div className="my-16 flex h-8 items-center justify-between gap-8 px-4">
<h1 className="text-2xl">Inputs</h1>
<Button href="/inputs/create" scroll={false} size="sm">
<PlusIcon className="-ml-0.5 w-5" />
<PlusIcon className="-ml-0.5 w-5 stroke-2" />
New input
</Button>
</div>
Expand Down
6 changes: 2 additions & 4 deletions app/(pages)/(with-nav)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import IconButton from '@/_components/icon-button';
import Subscriptions from '@/_components/subscriptions';
import countNotifications from '@/_queries/count-notifications';
import getCurrentUser from '@/_queries/get-current-user';
import getCustomer from '@/_queries/get-customer';
import BellIcon from '@heroicons/react/24/outline/BellIcon';
import { ReactNode } from 'react';

Expand All @@ -13,10 +12,9 @@ interface LayoutProps {
}

const Layout = async ({ children }: LayoutProps) => {
const [{ count }, user, { data: customer }] = await Promise.all([
const [{ count }, user] = await Promise.all([
countNotifications(),
getCurrentUser(),
getCustomer(),
]);

if (!user) return null;
Expand Down Expand Up @@ -65,7 +63,7 @@ const Layout = async ({ children }: LayoutProps) => {
}
scroll={false}
/>
<AccountMenu customer={customer} user={user} />
<AccountMenu user={user} />
</div>
</nav>
)}
Expand Down
30 changes: 0 additions & 30 deletions app/(pages)/(with-nav)/subjects/(list)/layout.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion app/(pages)/(with-nav)/subjects/(list)/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Spinner from '@/_components/spinner';

const Loading = () => <Spinner className="mx-auto" />;
const Loading = () => <Spinner className="mx-auto mt-16" />;

export default Loading;
63 changes: 46 additions & 17 deletions app/(pages)/(with-nav)/subjects/(list)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Button from '@/_components/button';
import CollapsibleArchive from '@/_components/collapsible-archive';
import Empty from '@/_components/empty';
import SubjectList from '@/_components/subject-list';
import SubscriptionStatus from '@/_constants/enum-subscription-status';
import getCurrentUser from '@/_queries/get-current-user';
import listSubjects, { ListSubjectsData } from '@/_queries/list-subjects';
import InformationCircleIcon from '@heroicons/react/24/outline/InformationCircleIcon';
import PlusIcon from '@heroicons/react/24/outline/PlusIcon';

export const metadata = { title: 'Subjects' };

Expand Down Expand Up @@ -42,24 +45,50 @@ const Page = async () => {

return (
<>
{!clientSubjects.length && !teamSubjects.length && (
<Empty className="mx-4">
<InformationCircleIcon className="w-7" />
Create a subject to get started.
</Empty>
)}
<SubjectList
clientSubjects={clientSubjects}
teamSubjects={teamSubjects}
/>
{(!!archivedTeamSubjects.length || !!archivedClientSubjects.length) && (
<CollapsibleArchive>
<SubjectList
clientSubjects={archivedClientSubjects}
teamSubjects={archivedTeamSubjects}
/>
</CollapsibleArchive>
{!user.user_metadata.is_client && (
<div className="mt-16 flex h-8 items-center justify-between gap-8 px-4">
<h1 className="text-2xl">Subjects</h1>
<Button
href={
user.app_metadata.subscription_status !==
SubscriptionStatus.Active && teamSubjects.length >= 2
? '/upgrade'
: '/subjects/create'
}
scroll={false}
size="sm"
>
<PlusIcon className="-ml-0.5 w-5 stroke-2" />
New subject
</Button>
</div>
)}
<div className="mt-16 space-y-4">
{!clientSubjects.length && !teamSubjects.length && (
<Empty className="mx-4">
<InformationCircleIcon className="w-7" />
{user.user_metadata.is_client
? 'No active subjects.'
: 'Create a subject to get started.'}
</Empty>
)}
<SubjectList
clientSubjects={clientSubjects}
teamSubjects={teamSubjects}
/>
{(!!archivedTeamSubjects.length || !!archivedClientSubjects.length) && (
<CollapsibleArchive>
<SubjectList
canUnarchive={
user.app_metadata.subscription_status ===
SubscriptionStatus.Active || teamSubjects.length < 2
}
clientSubjects={archivedClientSubjects}
teamSubjects={archivedTeamSubjects}
/>
</CollapsibleArchive>
)}
</div>
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(with-nav)/templates/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Layout = ({ children }: LayoutProps) => (
<div className="my-16 flex h-8 items-center justify-between gap-8 px-4">
<h1 className="text-2xl">Templates</h1>
<Button href="/templates/create" scroll={false} size="sm">
<PlusIcon className="-ml-0.5 w-5" />
<PlusIcon className="-ml-0.5 w-5 stroke-2" />
New template
</Button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/(pages)/(with-nav)/templates/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Page = async () => {
}

return (
<ul className="mx-4 rounded border border-alpha-1 bg-bg-2 py-1">
<ul className="mx-4 overflow-hidden rounded border border-alpha-1 bg-bg-2 py-1">
{templates.map((template) => (
<li
className="flex items-stretch hover:bg-alpha-1 active:bg-alpha-1"
Expand Down
5 changes: 5 additions & 0 deletions app/(pages)/@modal/(md)/upgrade/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import PageModalLoading from '@/_components/page-modal-loading';

const Loading = PageModalLoading;

export default Loading;
61 changes: 61 additions & 0 deletions app/(pages)/@modal/(md)/upgrade/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import BackButton from '@/_components/back-button';
import PageModalHeader from '@/_components/page-modal-header';
import UpgradePlanButton from '@/_components/upgrade-plan-button';
import getCurrentUser from '@/_queries/get-current-user';
import formatTitle from '@/_utilities/format-title';

export const metadata = { title: formatTitle(['Upgrade plan']) };

const Page = async () => {
const user = await getCurrentUser();
if (!user) return;

return (
<>
<PageModalHeader title="Upgrade plan" />
<div className="space-y-14 px-4 pb-8 pt-6 sm:px-8">
<table className="w-full">
<thead>
<tr>
<th></th>
<th className="pb-2 text-xl font-normal text-fg-4">Free</th>
<th className="pb-2 text-xl font-normal">Pro</th>
</tr>
</thead>
<tbody className="divide-y divide-alpha-1">
<tr>
<td className="py-2 text-fg-4">Active subject limit</td>
<td className="py-2 text-center text-fg-4">2</td>
<td className="py-2 text-center">Unlimited</td>
</tr>
<tr>
<td className="py-2 text-fg-4">Priority customer support</td>
<td className="py-2 text-center text-fg-4">x</td>
<td className="py-2 text-center"></td>
</tr>
<tr>
<td className="py-2 text-fg-4">Support our team</td>
<td className="py-2 text-center text-fg-4">x</td>
<td className="py-2 text-center"></td>
</tr>
<tr>
<td></td>
<td className="pt-2 text-center text-fg-4">$0</td>
<td className="pt-2 text-center">
$19<span className="text-xs"> / month</span>
</td>
</tr>
</tbody>
</table>
<div className="flex gap-4">
<BackButton className="w-full" colorScheme="transparent">
Close
</BackButton>
<UpgradePlanButton user={user} />
</div>
</div>
</>
);
};

export default Page;
68 changes: 33 additions & 35 deletions app/_components/account-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@

import Avatar from '@/_components/avatar';
import DropdownMenu from '@/_components/dropdown-menu';
import createCustomerCheckout from '@/_mutations/create-customer-checkout';
import SubscriptionStatus from '@/_constants/enum-subscription-status';
import signOut from '@/_mutations/sign-out';
import { GetCustomerData } from '@/_queries/get-customer';
import getCustomerBillingPortal from '@/_queries/get-customer-billing-portal';
import ArrowLeftStartOnRectangleIcon from '@heroicons/react/24/outline/ArrowLeftStartOnRectangleIcon';
import ArrowUpCircleIcon from '@heroicons/react/24/outline/ArrowUpCircleIcon';
import Bars3Icon from '@heroicons/react/24/outline/Bars3Icon';
import Cog6ToothIcon from '@heroicons/react/24/outline/Cog6ToothIcon';
import CreditCardIcon from '@heroicons/react/24/outline/CreditCardIcon';
import RocketLaunchIcon from '@heroicons/react/24/outline/RocketLaunchIcon';
import { User } from '@supabase/supabase-js';
import { useState, useTransition } from 'react';

interface AccountMenuProps {
customer: GetCustomerData;
user: User | null;
user: User;
}

const AccountMenu = ({ customer, user }: AccountMenuProps) => {
const AccountMenu = ({ user }: AccountMenuProps) => {
const [isSignOutTransitioning, startSignOutTransition] = useTransition();

const [isBillingRedirectLoading, setIsBillingRedirectLoading] =
useState(false);

const isSubscribed = customer?.subscription_status === 'active';
const isSubscribed =
user.app_metadata.subscription_status === SubscriptionStatus.Active;

return (
<DropdownMenu
Expand All @@ -45,34 +44,33 @@ const AccountMenu = ({ customer, user }: AccountMenuProps) => {
<Cog6ToothIcon className="w-5 text-fg-4" />
Account settings
</DropdownMenu.Button>
<DropdownMenu.Button
loading={isBillingRedirectLoading}
loadingText="Redirecting…"
onClick={async (e) => {
e.preventDefault();
setIsBillingRedirectLoading(true);

const { url } = await (isSubscribed
? getCustomerBillingPortal()
: createCustomerCheckout());

if (url) location.href = url;
else setIsBillingRedirectLoading(false);
}}
>
{isSubscribed ? (
<>
<CreditCardIcon className="w-5 text-fg-4" />
Manage subscription
</>
) : (
<>
<ArrowUpCircleIcon className="w-5 text-fg-4" />
Upgrade to pro
</>
)}
</DropdownMenu.Button>
<DropdownMenu.Separator />
{!user?.user_metadata?.is_client && (
<DropdownMenu.Button
href={isSubscribed ? undefined : '/upgrade'}
loading={isBillingRedirectLoading}
loadingText="Redirecting…"
onClick={async (e) => {
if (!isSubscribed) return;
e.preventDefault();
setIsBillingRedirectLoading(true);
const { url } = await getCustomerBillingPortal();
if (url) location.href = url;
else setIsBillingRedirectLoading(false);
}}
>
{isSubscribed ? (
<>
<CreditCardIcon className="w-5 text-fg-4" />
Manage billing
</>
) : (
<>
<RocketLaunchIcon className="w-5 text-fg-4" />
Upgrade plan
</>
)}
</DropdownMenu.Button>
)}
<DropdownMenu.Button
loading={isSignOutTransitioning}
loadingText="Signing out…"
Expand Down
2 changes: 1 addition & 1 deletion app/_components/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Content = React.forwardRef<
ref={ref}
sideOffset={4}
className={twMerge(
'z-10 w-60 rounded border border-alpha-2 bg-bg-3 py-1 drop-shadow',
'z-10 w-60 overflow-hidden rounded border border-alpha-2 bg-bg-3 py-1 drop-shadow',
className,
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion app/_components/event-types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const EventTypes = async ({ isTeamMember, subjectId }: EventTypesProps) => {
if (!eventTypes?.length) return null;

return (
<ul className="rounded border border-alpha-1 bg-bg-2 py-1">
<ul className="overflow-hidden rounded border border-alpha-1 bg-bg-2 py-1">
{eventTypes.map((eventType) => (
<li
className="flex items-stretch hover:bg-alpha-1 active:bg-alpha-1"
Expand Down
Loading

0 comments on commit 79c38bf

Please sign in to comment.