diff --git a/app/settings/components/billing/billing-history.tsx b/app/settings/components/billing/billing-history.tsx
new file mode 100644
index 0000000..f2da704
--- /dev/null
+++ b/app/settings/components/billing/billing-history.tsx
@@ -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 (
+
+
+
+
Billing history
+
+
+
+
+
+
+
+
+
+ Date
+ |
+
+ Description
+ |
+
+ Amount
+ |
+ {/*
+ `relative` is added here due to a weird bug in Safari that causes `sr-only` headings to introduce overflow on the body on mobile.
+ */}
+
+ View receipt
+ |
+
+
+
+ {payments.map((payment) => (
+
+
+
+ |
+
+ {payment.description}
+ |
+
+ {payment.amount}
+ |
+
+
+ View receipt
+
+ |
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/settings/components/paddle-link.tsx b/app/settings/components/billing/paddle-link.tsx
similarity index 100%
rename from app/settings/components/paddle-link.tsx
rename to app/settings/components/billing/paddle-link.tsx
diff --git a/app/settings/components/billing/plans.tsx b/app/settings/components/billing/plans.tsx
new file mode 100644
index 0000000..4f80bea
--- /dev/null
+++ b/app/settings/components/billing/plans.tsx
@@ -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 (
+
+ {pricing.tiers.map((tier) => {
+ const isCurrentTier = subscription?.paddlePlanId === tier.planId;
+ const cta = isCurrentTier ? "Current plan" : !!subscription ? `Switch to ${tier.title}` : "Subscribe";
+
+ return (
+
+
+
{tier.title}
+ {tier.yearly ? (
+
+ Get 2 months free!
+
+ ) : null}
+
+ {tier.price}€
+ {tier.frequency}
+
+ {tier.yearly ? (
+
Billed yearly ({tier.price * 12}€)
+ ) : null}
+
{tier.description}
+
+
+ {tier.features.map((feature) => (
+ -
+
+ {feature}
+
+ ))}
+ {tier.unavailableFeatures.map((feature) => (
+ -
+
+ {~feature.indexOf("(coming soon)")
+ ? feature.slice(0, feature.indexOf("(coming soon)"))
+ : feature}
+
+
+ ))}
+
+
+
+
+
+ );
+ })}
+
+ );
+}
+
+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,
+ },
+ ],
+};
diff --git a/app/settings/components/divider.tsx b/app/settings/components/divider.tsx
new file mode 100644
index 0000000..8c78520
--- /dev/null
+++ b/app/settings/components/divider.tsx
@@ -0,0 +1,9 @@
+export default function Divider() {
+ return (
+
+ );
+}
diff --git a/app/settings/hooks/use-subscription.ts b/app/settings/hooks/use-subscription.ts
index 1719aa4..ed247e6 100644
--- a/app/settings/hooks/use-subscription.ts
+++ b/app/settings/hooks/use-subscription.ts
@@ -1,15 +1,20 @@
import { useEffect, useRef } from "react";
import { useQuery, useMutation, useRouter, useSession } from "blitz";
+import type { Subscription } from "db";
import getSubscription from "../queries/get-subscription";
import usePaddle from "./use-paddle";
import useCurrentUser from "../../core/hooks/use-current-user";
import updateSubscription from "../mutations/update-subscription";
-export default function useSubscription() {
+type Params = {
+ initialData?: Subscription;
+};
+
+export default function useSubscription({ initialData }: Params = {}) {
const session = useSession();
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 router = useRouter();
diff --git a/app/settings/pages/settings/billing.tsx b/app/settings/pages/settings/billing.tsx
index e94bd70..caede72 100644
--- a/app/settings/pages/settings/billing.tsx
+++ b/app/settings/pages/settings/billing.tsx
@@ -1,63 +1,24 @@
import type { BlitzPage } from "blitz";
-import { GetServerSideProps, Link, Routes } from "blitz";
-import * as Panelbear from "@panelbear/panelbear-js";
-import clsx from "clsx";
+import { GetServerSideProps, getSession, Routes } from "blitz";
+import db, { Subscription, SubscriptionStatus } from "db";
import useSubscription from "../../hooks/use-subscription";
import useRequireOnboarding from "../../../core/hooks/use-require-onboarding";
import SettingsLayout from "../../components/settings-layout";
-import appLogger from "../../../../integrations/logger";
-import PaddleLink from "../../components/paddle-link";
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 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",
- 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,
- },
- ],
+type Props = {
+ subscription?: Subscription;
};
-const Billing: BlitzPage = () => {
+const Billing: BlitzPage = (props) => {
/*
TODO: I want to be able to
- subscribe
@@ -70,81 +31,16 @@ const Billing: BlitzPage = () => {
*/
useRequireOnboarding();
- const { subscription, cancelSubscription, updatePaymentMethod } = useSubscription();
- console.log("subscription", subscription);
+ const { subscription, cancelSubscription, updatePaymentMethod } = useSubscription({
+ initialData: props.subscription,
+ });
if (!subscription) {
return (
-
-
-
Subscribe
-
- Update your billing information. Please note that updating your location could affect your tax
- rates.
-
-
-
-
- {pricing.tiers.map((tier) => (
-
- ))}
-
-
+ <>
+
+ Prices include all applicable sales taxes.
+ >
);
}
@@ -153,100 +49,22 @@ const Billing: BlitzPage = () => {
updatePaymentMethod({ updateUrl: subscription.updateUrl })}
- text="Update payment method on Paddle"
+ text="Update payment method"
/>
-
-
- {/**/}
-
-
cancelSubscription({ cancelUrl: subscription.cancelUrl })}
- text="Cancel subscription on Paddle"
+ text="Cancel subscription"
/>
-
-
-
-
- Billing history
-
-
-
-
-
-
-
-
-
-
- Date
- |
-
- Description
- |
-
- Amount
- |
- {/*
- `relative` is added here due to a weird bug in Safari that causes `sr-only` headings to introduce overflow on the body on mobile.
- */}
-
- View receipt
- |
-
-
-
- {[
- {
- id: 1,
- date: new Date(),
- description: "",
- amount: "340 USD",
- href: "",
- },
- ].map((payment) => (
-
-
-
- |
-
- {payment.description}
- |
-
- {payment.amount}
- |
-
-
- View receipt
-
- |
-
- ))}
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Prices include all applicable sales taxes.
>
);
};
@@ -255,8 +73,21 @@ Billing.getLayout = (page) => {page};
Billing.authenticate = { redirectTo: Routes.SignIn() };
-export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
- return { props: {} };
+export const getServerSideProps: GetServerSideProps = 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: { subscription },
+ };
};
export default Billing;