In this section, we create a page route in Gatsbyjs where the user can manage their course subscription.
On the frontend (Reactjs) page, we need to list out all the active user's subscriptions. For that, we need to fetch the user's subscription data from the Sanity server.
To fetch the user's subscription data from the Sanity server, we can write the GROQ query as below:
*[_type == 'subscriptions' && customer._ref in *[_type=='customer' && email=='${email}']._id]
{_id, _type, customer, status, cancel_at_period_end, canceled_at, cancel_at, start_date, price,
'module': price->content->{_id, _type, exerpt, title, seo,
'img': seo.image.asset->{
_updatedAt,
extension,
originalFilename,
url},
slug
}}
We can use the above GROQ query in the React component to display the list of users' subscriptions.
To create the page route in Gatsby's, we can create the file at the below location.
/src/pages/settings.js
In the settings.js
, we can write the code as below:
import React, { useEffect, useState } from "react"
import PortableText from "@components/portabletext/portableText"
import Layout from "@components/layout"
import { navigate } from "gatsby"
import { isLoggedIn, getCurrentUser } from "@utils/auth"
import { querySanity } from "@lib/querySanity"
// import archieveSubscription from "@lib/sanity/archieveSubscription"
import Axios from "axios"
import toast, { Toaster } from "react-hot-toast"
const Settings = ({ location }) => {
const [subscriptions, setSubscriptions] = useState(null)
const [disable, setDisable] = useState(false)
useEffect(() => {
if (!isLoggedIn()) {
navigate("/login")
} else {
fetchSupscription()
}
}, [subscriptions])
async function fetchSupscription() {
let { email } = getCurrentUser()
let data = await querySanity(`
*[_type == 'subscriptions' && customer._ref in *[_type=='customer' && email=='${email}']._id]
{_id, _type, customer, status, cancel_at_period_end, canceled_at, cancel_at, start_date, price,
'module': price->content->{_id, _type, exerpt, title, seo,
'img': seo.image.asset->{
_updatedAt,
extension,
originalFilename,
url},
slug
}}
`)
setSubscriptions(data)
}
async function cancelSubscription(req) {
setDisable(true)
let is = window.confirm(req.confirmMsg)
if (is) {
let { token } = getCurrentUser()
try {
let { data, status } = await Axios.post(`/api/cancelSubscription`, {
token: token,
subID: req._id,
actionReq: req.actionReq,
})
setDisable(false)
if (data.message === "success" && status === 200) {
toast.success("Your request is fulfilled.")
}
setSubscriptions(null)
return data
} catch (error) {
setDisable(false)
toast.success("Something went wrong.", {
icon: "โ ๏ธ",
})
}
}
}
if (subscriptions === null) {
return (
<Layout location={location}>
<p>Please wait the data is loading!!!</p>
</Layout>
)
}
return (
<Layout location={location}>
<Toaster position="top-center" />
<div className="container max-w-2xl px-3 mx-auto">
<h1 className="mb-4 text-3xl font-extrabold leading-snug">
Your's Subsciption
</h1>
<section className="flex flex-wrap justify-between gap-4 antialiased">
{subscriptions.map(data => {
return (
<div className="w-5/12 h-full">
<div className="max-w-xs mx-auto">
<div className="flex flex-col h-full overflow-hidden bg-white rounded-lg shadow-lg">
<div className="flex flex-col flex-grow p-5">
<div className="flex-grow">
<header className="mb-6">
<a
className="block focus:outline-none focus-visible:ring-2"
href="#0"
>
<h3 className="text-xl font-extrabold leading-snug text-gray-900">
{data?.module?.title}
</h3>
</a>
</header>
{data?.cancel_at_period_end === true && (
<h3 className="py-1 text-xs italic font-light">
Canceled at {data?.canceled_at}
</h3>
)}
<div className="mb-8">
<p className="overflow-hidden text-clip">
{data?.module?.exerpt && (
<PortableText blocks={data?.module?.exerpt} />
)}
</p>
</div>
</div>
<div className="relative flex justify-end space-x-2">
{data?.status && (
<button class="font-semibold text-sm inline-flex items-center justify-center px-3 py-1.5 border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out bg-indigo-50 focus:outline-none focus-visible:ring-2 hover:bg-indigo-100 text-indigo-500">
{data.status}
</button>
)}
{data?.cancel_at_period_end === false && (
<button
className="font-semibold text-sm inline-flex items-center justify-center px-3 py-1.5 border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out bg-indigo-500 focus:outline-non text-white disabled:bg-gray-50 disabled:text-gray-500 disabled:border-gray-200 disabled:shadow-none disabled:pointer-events-none"
disabled={disable ? true : false}
onClick={() =>
cancelSubscription({
_id: data._id,
confirmMsg:
"Do you want to cancel the subsciption at the end of trial period.",
})
}
>
๐ Cancel at period end
</button>
)}
{data?.cancel_at_period_end === true && (
<button
className="font-semibold text-sm inline-flex items-center justify-center px-3 py-1.5 border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out bg-indigo-500 focus:outline-non text-white disabled:bg-gray-50 disabled:text-gray-500 disabled:border-gray-200 disabled:shadow-none disabled:pointer-events-none"
disabled={disable ? true : false}
onClick={() =>
cancelSubscription({
_id: data._id,
actionReq: "dont_cancel",
confirmMsg:
"Do you want to resume the subsciption.",
})
}
>
Resume subscription ยป
</button>
)}
</div>
</div>
</div>
</div>
</div>
)
})}
</section>
</div>
</Layout>
)
}
export default Settings
Let's break down the above code to understand it better.
The above code renders in the browser as below:
Depending on the user subscription the above page is rendered. In our case, we have two (2) subscription states as shown below.
In the above React component, we run the GROQ query when the component initially renders to fetch the user's subscription documents from the Sanity Server. View the below image for your reference.
After we receive the response data from the above query, we can update the react state and render the data in JSX as below:
We use Tailwind CSS to style the JSX elements. To visualize it, you can view the below Gif.
Now here is the fun part... We need to perform an action to change the subscription status when the user checks the action button. Here the user can perform two (2) actions to manage the subscription:
When the user clicks the action button, cancelSubscription
function in the React component. The code for the button in JSX is written as below:
When the user clicks the button, we send the HTTP request to /api/cancelSubscription
to manage the user subscription. For that, we can write the code as below:
The above function sends the HTTP request to the cancelSubscription
serverless function to change the user's subscription status in the Sanity and Stripe Server.
Finally, it's time to test the page... For that, view the below Gif.
Congrats ๐