feat: add guards and subscription ui

This commit is contained in:
Mythie
2023-05-06 16:08:21 +10:00
parent 7eaa00b836
commit 6934e573d5
13 changed files with 379 additions and 47 deletions
+72
View File
@@ -0,0 +1,72 @@
import { useMemo, useState } from "react";
import { classNames } from "@documenso/lib";
import { STRIPE_PLANS, fetchCheckoutSession, useSubscription } from "@documenso/lib/stripe";
import { Button } from "@documenso/ui";
import { Switch } from "@headlessui/react";
export const BillingPlans = () => {
const { subscription, hasSubscription, isLoading } = useSubscription();
const [isAnnual, setIsAnnual] = useState(true);
return (
<div>
{!subscription &&
STRIPE_PLANS.map((plan) => (
<div key={plan.name} className="rounded-lg border py-4 px-6">
<h3 className="text-center text-lg font-medium leading-6 text-gray-900">{plan.name}</h3>
<div className="my-4 flex justify-center">
<Switch.Group as="div" className="flex items-center">
<Switch
checked={isAnnual}
onChange={setIsAnnual}
className={classNames(
isAnnual ? "bg-neon-600" : "bg-gray-200",
"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-neon-600 focus:ring-offset-2"
)}>
<span
aria-hidden="true"
className={classNames(
isAnnual ? "translate-x-5" : "translate-x-0",
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
)}
/>
</Switch>
<Switch.Label as="span" className="ml-3 text-sm">
<span className="font-medium text-gray-900">Annual billing</span>{" "}
<span className="text-gray-500">(Save $60)</span>
</Switch.Label>
</Switch.Group>
</div>
<p className="mt-2 text-center text-gray-500">
${(isAnnual ? plan.prices.yearly.price : plan.prices.monthly.price).toFixed(2)}{" "}
<span className="text-sm text-gray-400">{isAnnual ? "/yr" : "/mo"}</span>
</p>
<p className="mt-4 text-center text-sm text-gray-500">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Corrupti voluptates delectus
doloremque hic vel!
</p>
<div className="mt-4">
<Button
className="w-full"
disabled={isLoading}
onClick={() =>
fetchCheckoutSession({
priceId: isAnnual ? plan.prices.yearly.priceId : plan.prices.monthly.priceId,
}).then((res) => {
if (res.success) {
window.location.href = res.url;
}
})
}>
Subscribe
</Button>
</div>
</div>
))}
</div>
);
};
+56 -7
View File
@@ -4,9 +4,18 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { updateUser } from "@documenso/features";
import { getUser } from "@documenso/lib/api";
import {
STRIPE_PLANS,
fetchCheckoutSession,
fetchPortalSession,
isSubscriptionsEnabled,
useSubscription,
} from "@documenso/lib/stripe";
import { SubscriptionStatus } from '@prisma/client'
import { Button } from "@documenso/ui";
import { CreditCardIcon, KeyIcon, UserCircleIcon } from "@heroicons/react/24/outline";
import { useSession } from "next-auth/react";
import { BillingPlans } from "./billing-plans";
const subNavigation = [
{
@@ -35,6 +44,7 @@ function classNames(...classes: any) {
export default function Setttings() {
const session = useSession();
const { subscription, hasSubscription } = useSubscription();
const [user, setUser] = useState({
email: "",
name: "",
@@ -179,24 +189,63 @@ export default function Setttings() {
</div>
<div
hidden={subNavigation.filter((e) => e.current)[0]?.name !== subNavigation?.[2]?.name}
hidden={
subNavigation.find((e) => e.current)?.name !== subNavigation?.[2]?.name
}
className="min-h-[251px] divide-y divide-gray-200 lg:col-span-9">
{/* Billing section */}
<div className="py-6 px-4 sm:p-6 lg:pb-8">
<div>
<h2 className="text-lg font-medium leading-6 text-gray-900">Billing</h2>
{!isSubscriptionsEnabled() && (
<p className="mt-2 text-sm text-gray-500">
Subscriptions are not enabled on this instance, you have nothing to do here.
</p>
)}
{isSubscriptionsEnabled() && <>
<p className="mt-1 text-sm text-gray-500">
Your subscription is currently <strong>{user?.subscription ? 'Active' : 'Inactive'}</strong>.
Your subscription is currently{" "}
<strong>{subscription?.status && subscription?.status !== SubscriptionStatus.INACTIVE ? "Active" : "Inactive"}</strong>.
</p>
{subscription?.status === SubscriptionStatus.PAST_DUE && (
<p className="mt-1 text-sm text-red-500">
Your subscription is past due. Please update your payment details to continue
using the service without interruption.
</p>
)}
<p className="mt-1 text-sm text-gray-500">
We use Stripe to process payments. Your card details are never stored on our
servers.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est consectetur magnam
illo aperiam expedita porro eos eum nam sapiente.
</p>
<div className="mt-8">
<Button onClick={() => {}}>Manage my subscription</Button>
</div>
<div className="mt-8">
<div className="grid grid-cols-1 lg:grid-cols-2">
<BillingPlans />
</div>
{subscription && (
<Button
onClick={() => {
if (isSubscriptionsEnabled() && subscription?.customerId) {
fetchPortalSession({
id: subscription.customerId,
}).then((res) => {
if (res.success) {
window.location.href = res.url;
}
});
}
}}>
Manage my subscription
</Button>
)}
</div>
</>}
</div>
</div>
</div>