Handle sub in dashboard

This commit is contained in:
Michael
2021-06-25 18:19:35 +02:00
parent c8fd50ab27
commit 4068f18852
6 changed files with 199 additions and 116 deletions

View File

@@ -4,49 +4,18 @@ You can add as many elements as you want. Don't forget to update your getProfile
function with your new elements.
*/
import { useEffect, useState } from "react";
import Avatar from "./Avatar";
import { PriceIds } from "utils/priceList";
import { supabase } from "../utils/supabaseClient";
import { toast } from "react-toastify";
import { useState } from "react";
export default function Account({ session }) {
const [loading, setLoading] = useState(true);
const [username, setUsername] = useState(null);
const [website, setWebsite] = useState(null);
export default function Account(props) {
const [loading, setLoading] = useState(false);
const [username, setUsername] = useState(props.profile.username);
const [website, setWebsite] = useState(props.profile.website);
const [avatar_url, setAvatarUrl] = useState(null);
useEffect(() => {
getProfile();
}, [session]);
async function getProfile() {
try {
setLoading(true);
const user = supabase.auth.user();
let { data, error, status } = await supabase
.from("profiles")
.select(`username, website, avatar_url`)
.eq("id", user.id)
.single();
if (error && status !== 406) {
throw error;
}
if (data) {
setUsername(data.username);
setWebsite(data.website);
setAvatarUrl(data.avatar_url);
}
} catch (error) {
alert(error.message);
} finally {
setLoading(false);
}
}
async function updateProfile({ username, website, avatar_url }) {
try {
setLoading(true);
@@ -71,12 +40,13 @@ export default function Account({ session }) {
alert(error.message);
} finally {
setLoading(false);
toast.success("Your profile has been updated")
toast.success("Your profile has been updated");
}
}
return (
<div className='form-widget mt-10 flex flex-col text-left'>
<div className='flex flex-col text-left w-full'>
<div className='max-w-sm flex flex-col justify-center m-auto w-full p-5'>
<Avatar
url={avatar_url}
size={150}
@@ -93,7 +63,7 @@ export default function Account({ session }) {
className='input input-primary input-bordered input-sm flex-1'
id='email'
type='text'
value={session.user.email}
value={props.session.user.email}
disabled
/>
</div>
@@ -131,5 +101,11 @@ export default function Account({ session }) {
</button>
</div>
</div>
<div className='max-w-xl flex flex-col justify-center m-auto w-full p-5 bordered border-2 border-primary shadow-lg my-5'>
<h2>Your current plan</h2>
<p>{props.plan.plan ? PriceIds[props.plan.plan] : "Free tier"}</p>
</div>
</div>
);
}

View File

@@ -6,6 +6,7 @@ You can switch between flat payment or subscription by setting the flat variable
import { useEffect, useState } from "react";
import { Auth } from "@supabase/ui";
import { Prices } from "utils/priceList";
import { Switch } from "@headlessui/react";
import axios from "axios";
import getStripe from "utils/stripe";
@@ -32,20 +33,7 @@ const Pricing = () => {
);
}
}, [user]);
const prices = {
personal: {
monthly: "price_1J5q2yDMjD0UnVmMXzEWYDnl",
anually: "price_1J5q45DMjD0UnVmMQxXHKGAv",
},
team: {
monthly: "price_1J5q3GDMjD0UnVmMlHc5Eedq",
anually: "price_1J5q8zDMjD0UnVmMqsngM91X",
},
pro: {
monthly: "price_1J5q3TDMjD0UnVmMJKX3nkDq",
anually: "price_1J5q9VDMjD0UnVmMIQtVDSZ9",
},
};
const flat = false; // Switch between subscription system or flat prices
const pricing = {
monthly: {
@@ -165,7 +153,9 @@ const Pricing = () => {
onClick={(e) =>
handleSubmit(
e,
enabled ? prices.personal.anually : prices.personal.monthly
enabled
? Prices.personal.anually.id
: Prices.personal.monthly.id
)
}>
Buy Now
@@ -206,7 +196,9 @@ const Pricing = () => {
onClick={(e) =>
handleSubmit(
e,
enabled ? prices.team.anually : prices.team.monthly
enabled
? Prices.team.anually.id
: Prices.team.monthly.id
)
}>
Buy Now
@@ -247,7 +239,9 @@ const Pricing = () => {
onClick={(e) =>
handleSubmit(
e,
enabled ? prices.pro.anually : prices.pro.monthly
enabled
? Prices.pro.anually.id
: Prices.pro.monthly.id
)
}>
Buy Now

View File

@@ -1,13 +1,14 @@
import { getSub, supabase } from "../utils/supabaseClient";
import { useEffect, useState } from "react";
import Auth from "../components/Auth";
import Dashboard from "../components/Dashboard";
import Head from "next/head";
import Layout from "components/Layout";
import { supabase } from "../utils/supabaseClient";
import { createClient } from "@supabase/supabase-js";
import { useRouter } from "next/router";
const DashboardPage = ({ user }) => {
const DashboardPage = ({ user, plan, profile }) => {
const [session, setSession] = useState(null);
const router = useRouter();
@@ -33,7 +34,7 @@ const DashboardPage = ({ user }) => {
</Head>
<Layout>
<div>
<>
<h1 className='text-4xl font-bold md:text-5xl font-title'>
Dashboard
</h1>
@@ -44,20 +45,42 @@ const DashboardPage = ({ user }) => {
</div>
) : (
<>
<Dashboard key={user.id} session={session} />
<Dashboard
key={user.id}
session={session}
plan={plan}
profile={profile}
/>
</>
)}
</>
</div>
</>
</Layout>
</div>
);
};
export async function getServerSideProps({ req }) {
const { user } = await supabase.auth.api.getUserByCookie(req);
const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_ADMIN_KEY
);
const { user } = await supabaseAdmin.auth.api.getUserByCookie(req);
// If the user exist, you will retrieve the user profile and if he/she's a paid user
if (user) {
return { props: { user } };
let { data: plan, error } = await supabaseAdmin
.from("subscriptions")
.select("paid_user, plan")
.eq("id", user.id)
.single();
let { data: profile, errorprofile } = await supabaseAdmin
.from("profiles")
.select(`username, website, avatar_url`)
.eq("id", user.id)
.single();
return { props: { user, plan, profile } };
}
if (!user) {

35
public/logopremium.svg Normal file
View File

@@ -0,0 +1,35 @@
<svg xmlns="http://www.w3.org/2000/svg" width="264.263" height="59.7" viewBox="0 0 264.263 59.7">
<g id="Groupe_14" data-name="Groupe 14" transform="translate(-6.737 -96.3)">
<g id="Groupe_13" data-name="Groupe 13" transform="translate(7.437 -99.899)">
<g id="Groupe_2" data-name="Groupe 2" transform="translate(72.056 206.798)">
<g id="Groupe_1" data-name="Groupe 1" transform="translate(0 0)">
<text id="SupaNexTail" transform="translate(-0.493 30.101)" fill="#a8a1a1" font-size="29" font-family="Poppins-ExtraBold, Poppins" font-weight="800"><tspan x="0" y="0">Supa</tspan><tspan y="0" fill="#37cdbe">Nex</tspan><tspan y="0">Tail</tspan></text>
</g>
</g>
<g id="Groupe_4" data-name="Groupe 4" transform="translate(0 196.899)">
<path id="Tracé_25" data-name="Tracé 25" d="M31.262,117.215c0,.8,9.054,1.456,20.222,1.456s20.222-.652,20.222-1.456-9.054-1.456-20.222-1.456-20.222.652-20.222,1.456Z" transform="translate(-25.89 -59.671)" fill="#45413c" opacity="0.15"/>
<path id="Tracé_26" data-name="Tracé 26" d="M70.055,53.76H30.106a1.78,1.78,0,0,0-1.781,1.78V81.385h43.51V55.543a1.78,1.78,0,0,0-1.78-1.783Z" transform="translate(-24.486 -30.037)" fill="#fff"/>
<path id="Tracé_27" data-name="Tracé 27" d="M71.835,71.3a7.5,7.5,0,0,0-10.108.483,4.386,4.386,0,0,0-1.264-.185,4.483,4.483,0,0,0-4.268,3.13,5.214,5.214,0,0,0-2.68,1.408,6.358,6.358,0,0,1-.69-2.695c1.784-3.3,3.663-7.351,3.664-9.768a14.682,14.682,0,0,0-.255-2.639l.127.137a2.53,2.53,0,0,0,4.166-.658,9.4,9.4,0,0,0,.531-6.745H39.106a9.408,9.408,0,0,0,.531,6.746,2.532,2.532,0,0,0,4.166.658l.128-.139a14.636,14.636,0,0,0-.255,2.636c0,2.418,1.879,6.472,3.663,9.776a6.358,6.358,0,0,1-.691,2.687,5.205,5.205,0,0,0-2.681-1.405A4.484,4.484,0,0,0,39.7,71.6a4.385,4.385,0,0,0-1.264.184A7.5,7.5,0,0,0,28.325,71.3V81.39h43.51Z" transform="translate(-24.486 -30.041)" fill="#e0e0e0"/>
<path id="Tracé_28" data-name="Tracé 28" d="M70.055,53.76H30.106a1.78,1.78,0,0,0-1.781,1.78V81.385h43.51V55.543a1.78,1.78,0,0,0-1.78-1.783Z" transform="translate(-24.486 -30.037)" fill="rgba(0,0,0,0)" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_29" data-name="Tracé 29" d="M71.576,59.648H34.251L34.209,84.2H71.576Z" transform="translate(-27.298 -32.851)" fill="#00b8f0"/>
<path id="Tracé_30" data-name="Tracé 30" d="M62.32,59.648,35.958,84.2H34.209l.042-24.552H62.32Z" transform="translate(-27.298 -32.851)" fill="#4acfff"/>
<path id="Tracé_31" data-name="Tracé 31" d="M69.86,72.384a7.486,7.486,0,0,0-5.32,2.211,4.384,4.384,0,0,0-1.264-.185,4.483,4.483,0,0,0-4.268,3.13,5.215,5.215,0,0,0-2.68,1.408,6.358,6.358,0,0,1-.69-2.695c1.784-3.3,3.663-7.351,3.664-9.768a14.681,14.681,0,0,0-.255-2.639l.127.137a2.53,2.53,0,0,0,4.166-.658,10.576,10.576,0,0,0,.932-3.674H41.518a10.569,10.569,0,0,0,.932,3.675,2.532,2.532,0,0,0,4.166.658l.128-.139a14.635,14.635,0,0,0-.255,2.636c0,2.418,1.879,6.472,3.663,9.776a6.358,6.358,0,0,1-.691,2.687A5.2,5.2,0,0,0,46.78,77.54a4.484,4.484,0,0,0-4.27-3.132,4.385,4.385,0,0,0-1.264.184,7.519,7.519,0,0,0-7.016-2.017L34.209,84.2H71.576V72.582A7.512,7.512,0,0,0,69.86,72.384Z" transform="translate(-27.298 -32.853)" fill="#009fd9"/>
<path id="Tracé_32" data-name="Tracé 32" d="M71.576,59.648H34.251L34.209,84.2H71.576Z" transform="translate(-27.298 -32.851)" fill="rgba(244,253,0,0.33)" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_33" data-name="Tracé 33" d="M72.16,109.749H20.971V107.57a.89.89,0,0,1,.892-.89h49.4a.89.89,0,0,1,.891.89Z" transform="translate(-20.971 -55.331)" fill="#f0f0f0" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_34" data-name="Tracé 34" d="M72.162,112.56a53.227,53.227,0,0,1-14.617,2.046H35.593a53.226,53.226,0,0,1-14.616-2.046Z" transform="translate(-20.973 -58.142)" fill="#bdbec0" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_35" data-name="Tracé 35" d="M66.1,87.274A5.815,5.815,0,0,0,61.232,89.9a2.771,2.771,0,0,0-4.5,2.18c0,.365.175.254-.506.254A3.539,3.539,0,0,0,52.8,95.019c-.81-1.013-2.651-1.839-2.651-6.732h-2.03c0,4.879-1.84,5.719-2.651,6.732a3.539,3.539,0,0,0-3.425-2.681c-.681,0-.506.109-.506-.254a2.771,2.771,0,0,0-4.5-2.179,5.821,5.821,0,1,0-8.783,7.5h41.76A5.818,5.818,0,0,0,66.1,87.274Z" transform="translate(-23.539 -46.054)" fill="#e8f4fa"/>
<path id="Tracé_36" data-name="Tracé 36" d="M32.166,89.806a5.815,5.815,0,0,1,4.869,2.631,2.771,2.771,0,0,1,4.5,2.18c0,.365-.175.254.506.254A3.529,3.529,0,0,1,45.418,97.4h.188c.951-.957,3.527-1.944,3.527-6.584,0,4.654,2.576,5.627,3.527,6.584h.188a3.529,3.529,0,0,1,3.374-2.532c.682,0,.506.109.506-.254a2.771,2.771,0,0,1,4.5-2.179,5.82,5.82,0,0,1,10.552,1.93,5.823,5.823,0,0,0-10.552-4.462,2.771,2.771,0,0,0-4.5,2.179c0,.365.175.254-.506.254A3.539,3.539,0,0,0,52.8,95.019c-.81-1.014-2.651-1.839-2.651-6.732H48.12c0,4.879-1.84,5.719-2.651,6.732a3.539,3.539,0,0,0-3.425-2.681c-.682,0-.506.109-.506-.254a2.771,2.771,0,0,0-4.5-2.179,5.822,5.822,0,0,0-10.552,4.462,5.824,5.824,0,0,1,5.681-4.562Z" transform="translate(-23.538 -46.054)" fill="#fff"/>
<path id="Tracé_37" data-name="Tracé 37" d="M66.1,87.274A5.815,5.815,0,0,0,61.232,89.9a2.771,2.771,0,0,0-4.5,2.18c0,.365.175.254-.506.254A3.539,3.539,0,0,0,52.8,95.019c-.81-1.013-2.651-1.839-2.651-6.732h-2.03c0,4.879-1.84,5.719-2.651,6.732a3.539,3.539,0,0,0-3.425-2.681c-.681,0-.506.109-.506-.254a2.771,2.771,0,0,0-4.5-2.179,5.821,5.821,0,1,0-8.783,7.5h41.76A5.818,5.818,0,0,0,66.1,87.274Z" transform="translate(-23.539 -46.054)" fill="rgba(0,0,0,0)" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_38" data-name="Tracé 38" d="M47.648,96.765a3.546,3.546,0,0,1,3.206-2.027m-6.079-.254a2.781,2.781,0,0,1,1.063-2.192m22.4,4.473a3.546,3.546,0,0,0-3.206-2.027m6.079-.254a2.777,2.777,0,0,0-1.063-2.192" transform="translate(-32.348 -48.454)" fill="rgba(0,0,0,0)" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_39" data-name="Tracé 39" d="M68.827,60.116h-6.29a17.681,17.681,0,0,0-1.573,6.59c0,2.488,2.688,7.608,4,9.966a.854.854,0,0,0,1.419,0C67.7,74.314,70.4,69.2,70.4,66.707A17.682,17.682,0,0,0,68.827,60.116Z" transform="translate(-40.087 -33.075)" fill="#ffaa54" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_40" data-name="Tracé 40" d="M69.29,60.115s1.963,4.391-.084,7.662a.884.884,0,0,1-1.408,0c-2.046-3.273-.081-7.663-.081-7.663Z" transform="translate(-42.908 -33.074)" fill="#ffe500" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_41" data-name="Tracé 41" d="M70.506,18.769A8.732,8.732,0,0,0,68.291,12L65.6,9.031a2.184,2.184,0,0,0-3.235,0L59.667,12a8.732,8.732,0,0,0-2.217,6.768l1.723,16.59h9.611Z" transform="translate(-38.384 -8.315)" fill="#fff" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_42" data-name="Tracé 42" d="M56.3,53.87l-2.716,2.948a.843.843,0,0,1-1.389-.219c-.941-2.055-2.154-6.572,3.4-9.549Zm9.609,0,2.716,2.948A.843.843,0,0,0,70.01,56.6c.941-2.055,2.154-6.572-3.4-9.549ZM55.781,48.9l.516,4.97h9.609l.516-4.97H55.781Z" transform="translate(-35.507 -26.829)" fill="#ff6242" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
<path id="Tracé_43" data-name="Tracé 43" d="M64.459,23.923a2.893,2.893,0,1,0,2.893-2.893A2.893,2.893,0,0,0,64.459,23.923Z" transform="translate(-41.757 -14.393)" fill="#00b8f0"/>
<path id="Tracé_44" data-name="Tracé 44" d="M65.3,25.966A2.894,2.894,0,0,1,69.4,21.873Z" transform="translate(-41.756 -14.39)" fill="#4acfff"/>
<path id="Tracé_45" data-name="Tracé 45" d="M64.459,23.923a2.893,2.893,0,1,0,2.893-2.893A2.893,2.893,0,0,0,64.459,23.923Zm2.893,10.688V45.248" transform="translate(-41.757 -14.393)" fill="rgba(0,0,0,0)" stroke="#45413c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
</g>
</g>
<text id="Premium" transform="translate(234 151)" fill="#f8dd4b" font-size="15" font-family="Poppins-ExtraBold, Poppins" font-weight="800"><tspan x="-36.202" y="0">Premium</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

45
utils/priceList.js Normal file
View File

@@ -0,0 +1,45 @@
// You can store your price IDs from Stripe here
const Prices = {
personal: {
monthly: {
id: "price_1J5q2yDMjD0UnVmMXzEWYDnl",
desc: "Personal plan (monthly)",
},
anually: {
id: "price_1J5q45DMjD0UnVmMQxXHKGAv",
desc: "Personal plan (anually)",
},
},
team: {
monthly: {
id: "price_1J5q3GDMjD0UnVmMlHc5Eedq",
desc: "Team plan (monthly)",
},
anually: {
id: "price_1J5q8zDMjD0UnVmMqsngM91X",
desc: "Team plan (anually)",
},
},
pro: {
monthly: {
id: "price_1J5q3TDMjD0UnVmMJKX3nkDq",
desc: "Pro plan (monthly)",
},
anually: {
id: "price_1J5q9VDMjD0UnVmMIQtVDSZ9",
desc: "Pro plan (anually)",
},
},
};
const PriceIds = {
price_1J5q2yDMjD0UnVmMXzEWYDnl: "Personal plan (monthly)",
price_1J5q45DMjD0UnVmMQxXHKGAv: "Personal plan (anually)",
price_1J5q3GDMjD0UnVmMlHc5Eedq: "Team plan (monthly)",
price_1J5q8zDMjD0UnVmMqsngM91X: "Team plan (anually)",
price_1J5q3TDMjD0UnVmMJKX3nkDq: "Pro plan (monthly)",
price_1J5q9VDMjD0UnVmMIQtVDSZ9: "Pro plan (anually)",
};
export { Prices, PriceIds };

View File

@@ -1,6 +1,16 @@
import { createClient } from '@supabase/supabase-js'
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Check if a user has a paid plan
export const getSub = async () => {
let { data: subscriptions, error } = await supabase
.from("subscriptions")
.select("paid_user, plan");
if (subscriptions) {
return subscriptions;
}
};