pricing plans in settings
This commit is contained in:
parent
13ac4a5580
commit
5172ab11e7
99
app/settings/components/billing/billing-history.tsx
Normal file
99
app/settings/components/billing/billing-history.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
const payments = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
date: new Date(),
|
||||||
|
description: "",
|
||||||
|
amount: "340 USD",
|
||||||
|
href: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
date: new Date(),
|
||||||
|
description: "",
|
||||||
|
amount: "340 USD",
|
||||||
|
href: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
date: new Date(),
|
||||||
|
description: "",
|
||||||
|
amount: "340 USD",
|
||||||
|
href: "",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function BillingHistory() {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="bg-white pt-6 shadow sm:rounded-md sm:overflow-hidden">
|
||||||
|
<div className="px-4 sm:px-6">
|
||||||
|
<h2 className="text-lg leading-6 font-medium text-gray-900">Billing history</h2>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6 flex flex-col">
|
||||||
|
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||||
|
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
||||||
|
<div className="overflow-hidden border-t border-gray-200">
|
||||||
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||||
|
>
|
||||||
|
Date
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||||
|
>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||||
|
>
|
||||||
|
Amount
|
||||||
|
</th>
|
||||||
|
{/*
|
||||||
|
`relative` is added here due to a weird bug in Safari that causes `sr-only` headings to introduce overflow on the body on mobile.
|
||||||
|
*/}
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="relative px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||||
|
>
|
||||||
|
<span className="sr-only">View receipt</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{payments.map((payment) => (
|
||||||
|
<tr key={payment.id}>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
<time>{payment.date.toDateString()}</time>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{payment.description}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{payment.amount}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<a
|
||||||
|
href={payment.href}
|
||||||
|
className="text-primary-600 hover:text-primary-900"
|
||||||
|
>
|
||||||
|
View receipt
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
126
app/settings/components/billing/plans.tsx
Normal file
126
app/settings/components/billing/plans.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { HiCheck } from "react-icons/hi";
|
||||||
|
import * as Panelbear from "@panelbear/panelbear-js";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
import useSubscription from "../../hooks/use-subscription";
|
||||||
|
|
||||||
|
export default function Plans() {
|
||||||
|
const { subscription, subscribe } = useSubscription();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-6 flex flex-row-reverse flex-wrap-reverse gap-x-4">
|
||||||
|
{pricing.tiers.map((tier) => {
|
||||||
|
const isCurrentTier = subscription?.paddlePlanId === tier.planId;
|
||||||
|
const cta = isCurrentTier ? "Current plan" : !!subscription ? `Switch to ${tier.title}` : "Subscribe";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={tier.title}
|
||||||
|
className={clsx(
|
||||||
|
tier.yearly && "mb-4",
|
||||||
|
"relative p-4 bg-white border border-gray-200 rounded-xl shadow-sm flex flex-grow w-1/3 flex-col",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-xl font-mackinac font-semibold text-gray-900">{tier.title}</h3>
|
||||||
|
{tier.yearly ? (
|
||||||
|
<p className="absolute top-0 py-1.5 px-4 bg-primary-500 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform -translate-y-1/2">
|
||||||
|
Get 2 months free!
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<p className="mt-4 flex items-baseline text-gray-900">
|
||||||
|
<span className="text-2xl font-extrabold tracking-tight">{tier.price}€</span>
|
||||||
|
<span className="ml-1 text-lg font-semibold">{tier.frequency}</span>
|
||||||
|
</p>
|
||||||
|
{tier.yearly ? (
|
||||||
|
<p className="text-gray-500 text-sm">Billed yearly ({tier.price * 12}€)</p>
|
||||||
|
) : null}
|
||||||
|
<p className="mt-6 text-gray-500">{tier.description}</p>
|
||||||
|
|
||||||
|
<ul role="list" className="mt-6 space-y-6">
|
||||||
|
{tier.features.map((feature) => (
|
||||||
|
<li key={feature} className="flex">
|
||||||
|
<HiCheck className="flex-shrink-0 w-6 h-6 text-[#0eb56f]" aria-hidden="true" />
|
||||||
|
<span className="ml-3 text-gray-500">{feature}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{tier.unavailableFeatures.map((feature) => (
|
||||||
|
<li key={feature} className="flex">
|
||||||
|
<span className="ml-9 text-gray-400">
|
||||||
|
{~feature.indexOf("(coming soon)")
|
||||||
|
? feature.slice(0, feature.indexOf("(coming soon)"))
|
||||||
|
: feature}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={isCurrentTier}
|
||||||
|
onClick={() => {
|
||||||
|
subscribe({ planId: tier.planId });
|
||||||
|
Panelbear.track(`Subscribe to ${tier.title}`);
|
||||||
|
}}
|
||||||
|
className={clsx(
|
||||||
|
!isCurrentTier
|
||||||
|
? "bg-primary-500 text-white hover:bg-primary-600"
|
||||||
|
: "bg-primary-50 text-primary-700 cursor-not-allowed",
|
||||||
|
"mt-8 block w-full py-3 px-6 border border-transparent rounded-md text-center font-medium",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{cta}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paidFeatures = [
|
||||||
|
"SMS",
|
||||||
|
"MMS (coming soon)",
|
||||||
|
"Calls",
|
||||||
|
"SMS forwarding (coming soon)",
|
||||||
|
"Call forwarding (coming soon)",
|
||||||
|
"Voicemail (coming soon)",
|
||||||
|
"Call recording (coming soon)",
|
||||||
|
];
|
||||||
|
const pricing = {
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
title: "Free",
|
||||||
|
planId: "free",
|
||||||
|
price: 0,
|
||||||
|
frequency: "",
|
||||||
|
description: "The essentials to let you try Shellphone.",
|
||||||
|
features: ["SMS (send only)"],
|
||||||
|
unavailableFeatures: paidFeatures.slice(1),
|
||||||
|
cta: "Subscribe",
|
||||||
|
yearly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Monthly",
|
||||||
|
planId: "727540",
|
||||||
|
price: 15,
|
||||||
|
frequency: "/month",
|
||||||
|
description: "Text and call anyone, anywhere in the world.",
|
||||||
|
features: paidFeatures,
|
||||||
|
unavailableFeatures: [],
|
||||||
|
cta: "Subscribe",
|
||||||
|
yearly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Yearly",
|
||||||
|
planId: "727544",
|
||||||
|
price: 12.5,
|
||||||
|
frequency: "/month",
|
||||||
|
description: "Text and call anyone, anywhere in the world, all year long.",
|
||||||
|
features: paidFeatures,
|
||||||
|
unavailableFeatures: [],
|
||||||
|
cta: "Subscribe",
|
||||||
|
yearly: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
9
app/settings/components/divider.tsx
Normal file
9
app/settings/components/divider.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default function Divider() {
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<div className="w-full border-t border-gray-300" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,15 +1,20 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { useQuery, useMutation, useRouter, useSession } from "blitz";
|
import { useQuery, useMutation, useRouter, useSession } from "blitz";
|
||||||
|
|
||||||
|
import type { Subscription } from "db";
|
||||||
import getSubscription from "../queries/get-subscription";
|
import getSubscription from "../queries/get-subscription";
|
||||||
import usePaddle from "./use-paddle";
|
import usePaddle from "./use-paddle";
|
||||||
import useCurrentUser from "../../core/hooks/use-current-user";
|
import useCurrentUser from "../../core/hooks/use-current-user";
|
||||||
import updateSubscription from "../mutations/update-subscription";
|
import updateSubscription from "../mutations/update-subscription";
|
||||||
|
|
||||||
export default function useSubscription() {
|
type Params = {
|
||||||
|
initialData?: Subscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useSubscription({ initialData }: Params = {}) {
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { user } = useCurrentUser();
|
const { user } = useCurrentUser();
|
||||||
const [subscription] = useQuery(getSubscription, null, { enabled: Boolean(session.orgId) });
|
const [subscription] = useQuery(getSubscription, null, { enabled: Boolean(session.orgId), initialData });
|
||||||
const [updateSubscriptionMutation] = useMutation(updateSubscription);
|
const [updateSubscriptionMutation] = useMutation(updateSubscription);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -1,63 +1,24 @@
|
|||||||
import type { BlitzPage } from "blitz";
|
import type { BlitzPage } from "blitz";
|
||||||
import { GetServerSideProps, Link, Routes } from "blitz";
|
import { GetServerSideProps, getSession, Routes } from "blitz";
|
||||||
import * as Panelbear from "@panelbear/panelbear-js";
|
|
||||||
import clsx from "clsx";
|
|
||||||
|
|
||||||
|
import db, { Subscription, SubscriptionStatus } from "db";
|
||||||
import useSubscription from "../../hooks/use-subscription";
|
import useSubscription from "../../hooks/use-subscription";
|
||||||
import useRequireOnboarding from "../../../core/hooks/use-require-onboarding";
|
import useRequireOnboarding from "../../../core/hooks/use-require-onboarding";
|
||||||
import SettingsLayout from "../../components/settings-layout";
|
import SettingsLayout from "../../components/settings-layout";
|
||||||
import appLogger from "../../../../integrations/logger";
|
|
||||||
import PaddleLink from "../../components/paddle-link";
|
|
||||||
import SettingsSection from "../../components/settings-section";
|
import SettingsSection from "../../components/settings-section";
|
||||||
import { HiCheck } from "react-icons/hi";
|
import Divider from "../../components/divider";
|
||||||
|
import PaddleLink from "../../components/billing/paddle-link";
|
||||||
|
import Plans from "../../components/billing/plans";
|
||||||
|
import BillingHistory from "../../components/billing/billing-history";
|
||||||
|
import appLogger from "../../../../integrations/logger";
|
||||||
|
|
||||||
const logger = appLogger.child({ page: "/account/settings/billing" });
|
const logger = appLogger.child({ page: "/account/settings/billing" });
|
||||||
|
|
||||||
const paidFeatures = [
|
type Props = {
|
||||||
"SMS",
|
subscription?: Subscription;
|
||||||
"MMS (coming soon)",
|
|
||||||
"Calls",
|
|
||||||
"SMS forwarding (coming soon)",
|
|
||||||
"Call forwarding (coming soon)",
|
|
||||||
"Voicemail (coming soon)",
|
|
||||||
"Call recording (coming soon)",
|
|
||||||
];
|
|
||||||
const pricing = {
|
|
||||||
tiers: [
|
|
||||||
{
|
|
||||||
title: "Free",
|
|
||||||
price: 0,
|
|
||||||
frequency: "",
|
|
||||||
description: "The essentials to let you try Shellphone.",
|
|
||||||
features: ["SMS (send only)"],
|
|
||||||
unavailableFeatures: paidFeatures.slice(1),
|
|
||||||
cta: "Current tier",
|
|
||||||
yearly: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Monthly",
|
|
||||||
price: 15,
|
|
||||||
frequency: "/month",
|
|
||||||
description: "Text and call anyone, anywhere in the world.",
|
|
||||||
features: paidFeatures,
|
|
||||||
unavailableFeatures: [],
|
|
||||||
cta: "Subscribe",
|
|
||||||
yearly: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Yearly",
|
|
||||||
price: 12.5,
|
|
||||||
frequency: "/month",
|
|
||||||
description: "Text and call anyone, anywhere in the world, all year long.",
|
|
||||||
features: paidFeatures,
|
|
||||||
unavailableFeatures: [],
|
|
||||||
cta: "Subscribe",
|
|
||||||
yearly: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Billing: BlitzPage = () => {
|
const Billing: BlitzPage<Props> = (props) => {
|
||||||
/*
|
/*
|
||||||
TODO: I want to be able to
|
TODO: I want to be able to
|
||||||
- subscribe
|
- subscribe
|
||||||
@ -70,81 +31,16 @@ const Billing: BlitzPage = () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
useRequireOnboarding();
|
useRequireOnboarding();
|
||||||
const { subscription, cancelSubscription, updatePaymentMethod } = useSubscription();
|
const { subscription, cancelSubscription, updatePaymentMethod } = useSubscription({
|
||||||
console.log("subscription", subscription);
|
initialData: props.subscription,
|
||||||
|
});
|
||||||
|
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
return (
|
return (
|
||||||
<SettingsSection>
|
<>
|
||||||
<div>
|
<Plans />
|
||||||
<h2 className="text-lg leading-6 font-medium text-gray-900">Subscribe</h2>
|
<p className="text-sm text-gray-500">Prices include all applicable sales taxes.</p>
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
</>
|
||||||
Update your billing information. Please note that updating your location could affect your tax
|
|
||||||
rates.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-6 flex flex-row-reverse flex-wrap-reverse gap-x-4">
|
|
||||||
{pricing.tiers.map((tier) => (
|
|
||||||
<div
|
|
||||||
key={tier.title}
|
|
||||||
className="relative p-4 mb-4 bg-white border border-gray-200 rounded-xl shadow-sm flex flex-grow w-1/3 flex-col"
|
|
||||||
>
|
|
||||||
<div className="flex-1">
|
|
||||||
<h3 className="text-xl font-mackinac font-semibold text-gray-900">{tier.title}</h3>
|
|
||||||
{tier.yearly ? (
|
|
||||||
<p className="absolute top-0 py-1.5 px-4 bg-primary-500 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform -translate-y-1/2">
|
|
||||||
Get 2 months free!
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
<p className="mt-4 flex items-baseline text-gray-900">
|
|
||||||
<span className="text-2xl font-extrabold tracking-tight">{tier.price}€</span>
|
|
||||||
<span className="ml-1 text-lg font-semibold">{tier.frequency}</span>
|
|
||||||
</p>
|
|
||||||
{tier.yearly ? (
|
|
||||||
<p className="text-gray-500 text-sm">Billed yearly ({tier.price * 12}€)</p>
|
|
||||||
) : null}
|
|
||||||
<p className="mt-6 text-gray-500">{tier.description}</p>
|
|
||||||
|
|
||||||
<ul role="list" className="mt-6 space-y-6">
|
|
||||||
{tier.features.map((feature) => (
|
|
||||||
<li key={feature} className="flex">
|
|
||||||
<HiCheck
|
|
||||||
className="flex-shrink-0 w-6 h-6 text-[#0eb56f]"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span className="ml-3 text-gray-500">{feature}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{tier.unavailableFeatures.map((feature) => (
|
|
||||||
<li key={feature} className="flex">
|
|
||||||
<span className="ml-9 text-gray-400">
|
|
||||||
{~feature.indexOf("(coming soon)")
|
|
||||||
? feature.slice(0, feature.indexOf("(coming soon)"))
|
|
||||||
: feature}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Link href={Routes.LandingPage({ join_waitlist: "" })}>
|
|
||||||
<a
|
|
||||||
onClick={() => Panelbear.track("redirect-to-join-waitlist")}
|
|
||||||
className={clsx(
|
|
||||||
tier.yearly
|
|
||||||
? "bg-primary-500 text-white hover:bg-primary-600"
|
|
||||||
: "bg-primary-50 text-primary-700 hover:bg-primary-100",
|
|
||||||
"mt-8 block w-full py-3 px-6 border border-transparent rounded-md text-center font-medium",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tier.cta}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,100 +49,22 @@ const Billing: BlitzPage = () => {
|
|||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<PaddleLink
|
<PaddleLink
|
||||||
onClick={() => updatePaymentMethod({ updateUrl: subscription.updateUrl })}
|
onClick={() => updatePaymentMethod({ updateUrl: subscription.updateUrl })}
|
||||||
text="Update payment method on Paddle"
|
text="Update payment method"
|
||||||
/>
|
/>
|
||||||
</SettingsSection>
|
|
||||||
|
|
||||||
<SettingsSection>{/*<BillingPlans activePlanId={subscription.paddlePlanId} />*/}</SettingsSection>
|
|
||||||
|
|
||||||
<SettingsSection>
|
|
||||||
<PaddleLink
|
<PaddleLink
|
||||||
onClick={() => cancelSubscription({ cancelUrl: subscription.cancelUrl })}
|
onClick={() => cancelSubscription({ cancelUrl: subscription.cancelUrl })}
|
||||||
text="Cancel subscription on Paddle"
|
text="Cancel subscription"
|
||||||
/>
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
<section aria-labelledby="billing-history-heading">
|
<BillingHistory />
|
||||||
<div className="bg-white pt-6 shadow sm:rounded-md sm:overflow-hidden">
|
|
||||||
<div className="px-4 sm:px-6">
|
<div className="hidden lg:block lg:py-3">
|
||||||
<h2 id="billing-history-heading" className="text-lg leading-6 font-medium text-gray-900">
|
<Divider />
|
||||||
Billing history
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex flex-col">
|
|
||||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<Plans />
|
||||||
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
<p className="text-sm text-gray-500">Prices include all applicable sales taxes.</p>
|
||||||
<div className="overflow-hidden border-t border-gray-200">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
|
||||||
>
|
|
||||||
Date
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
|
||||||
>
|
|
||||||
Description
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
|
||||||
>
|
|
||||||
Amount
|
|
||||||
</th>
|
|
||||||
{/*
|
|
||||||
`relative` is added here due to a weird bug in Safari that causes `sr-only` headings to introduce overflow on the body on mobile.
|
|
||||||
*/}
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="relative px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
|
||||||
>
|
|
||||||
<span className="sr-only">View receipt</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{[
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
date: new Date(),
|
|
||||||
description: "",
|
|
||||||
amount: "340 USD",
|
|
||||||
href: "",
|
|
||||||
},
|
|
||||||
].map((payment) => (
|
|
||||||
<tr key={payment.id}>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
<time>{payment.date}</time>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{payment.description}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{payment.amount}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<a
|
|
||||||
href={payment.href}
|
|
||||||
className="text-primary-600 hover:text-primary-900"
|
|
||||||
>
|
|
||||||
View receipt
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -255,8 +73,21 @@ Billing.getLayout = (page) => <SettingsLayout>{page}</SettingsLayout>;
|
|||||||
|
|
||||||
Billing.authenticate = { redirectTo: Routes.SignIn() };
|
Billing.authenticate = { redirectTo: Routes.SignIn() };
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }) => {
|
||||||
|
const session = await getSession(req, res);
|
||||||
|
const subscription = await db.subscription.findFirst({
|
||||||
|
where: {
|
||||||
|
organizationId: session.orgId,
|
||||||
|
status: SubscriptionStatus.active,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!subscription) {
|
||||||
return { props: {} };
|
return { props: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: { subscription },
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Billing;
|
export default Billing;
|
||||||
|
Loading…
Reference in New Issue
Block a user