mirror of
https://github.com/fergalmoran/supanextail.git
synced 2025-12-22 09:17:54 +00:00
Add Poppins font + Pricing page + Improve comments + Avatar system (WIP)
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
/*
|
||||
This is the Auth component. It will allow your user to login.
|
||||
By default, it is available with the auth.js page, but you can use it everywhere you want!
|
||||
|
||||
CONFIGURE THE AUTH COMPONENT LINE 30
|
||||
You can select your auth providers, or just keep the email/password. You can
|
||||
check the providers available here: https://supabase.io/docs/guides/auth
|
||||
*/
|
||||
import { Auth } from "@supabase/ui";
|
||||
import { supabase } from "utils/supabaseClient";
|
||||
@@ -9,14 +13,14 @@ const Container = (props) => {
|
||||
const { user } = Auth.useUser();
|
||||
if (user)
|
||||
return (
|
||||
<>
|
||||
<div className='w-80 md:w-96 order-first lg:order-last'>
|
||||
<p>Hello {user.email}! 👋 You are already logged in</p>
|
||||
<button
|
||||
className='btn btn-primary'
|
||||
onClick={() => props.supabaseClient.auth.signOut()}>
|
||||
Sign out
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
return props.children;
|
||||
};
|
||||
@@ -25,7 +29,14 @@ const AuthComponent = () => {
|
||||
return (
|
||||
<Auth.UserContextProvider supabaseClient={supabase}>
|
||||
<Container supabaseClient={supabase}>
|
||||
<Auth supabaseClient={supabase} />
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
providers={["google"]}
|
||||
socialLayout='horizontal'
|
||||
socialButtonSize='xlarge'
|
||||
socialColors={true}
|
||||
className='p-5'
|
||||
/>
|
||||
</Container>
|
||||
</Auth.UserContextProvider>
|
||||
);
|
||||
|
||||
24
components/AuthText.js
Normal file
24
components/AuthText.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import Image from "next/image";
|
||||
import authImage from "public/auth.png";
|
||||
|
||||
const AuthText = () => {
|
||||
return (
|
||||
<div className='lg:mt-0 max-w-lg flex flex-col text-xl'>
|
||||
<div className='mt-10 mb-3'>
|
||||
<Image
|
||||
src={authImage}
|
||||
width={authImage.width / 1.5}
|
||||
height={authImage.height / 1.5}
|
||||
/>
|
||||
</div>
|
||||
<h2 className='text-4xl font-title font-semibold'>
|
||||
Join SupaNexTail for <span className='text-accent'>free</span>!
|
||||
</h2>
|
||||
<p className='mb-5 mt-8'>
|
||||
Create your website in a few minutes with our boilerplate.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthText;
|
||||
90
components/Avatar.js
Normal file
90
components/Avatar.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { supabase } from "utils/supabaseClient";
|
||||
|
||||
const Avatar = ({ url, size, onUpload }) => {
|
||||
const [avatarUrl, setAvatarUrl] = useState(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (url) downloadImage(url);
|
||||
}, [url]);
|
||||
|
||||
async function downloadImage(path) {
|
||||
try {
|
||||
const { data, error } = await supabase.storage
|
||||
.from("avatars")
|
||||
.download(path);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
const url = URL.createObjectURL(data);
|
||||
setAvatarUrl(url);
|
||||
} catch (error) {
|
||||
console.log("Error downloading image: ", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadAvatar(event) {
|
||||
try {
|
||||
setUploading(true);
|
||||
|
||||
if (!event.target.files || event.target.files.length === 0) {
|
||||
throw new Error("You must select an image to upload.");
|
||||
}
|
||||
|
||||
const file = event.target.files[0];
|
||||
const fileExt = file.name.split(".").pop();
|
||||
const fileName = `${Math.random()}.${fileExt}`;
|
||||
const filePath = `${fileName}`;
|
||||
|
||||
let { error: uploadError } = await supabase.storage
|
||||
.from("avatars")
|
||||
.upload(filePath, file);
|
||||
|
||||
if (uploadError) {
|
||||
throw uploadError;
|
||||
}
|
||||
|
||||
onUpload(filePath);
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='m-auto mb-5'>
|
||||
{avatarUrl ? (
|
||||
<img
|
||||
src={avatarUrl}
|
||||
alt='Avatar'
|
||||
className='avatar rounded-full w-28 h-28 flex m-auto'
|
||||
/>
|
||||
) : (
|
||||
<div className='avatar rounded-full w-28 h-28' />
|
||||
)}
|
||||
<div style={{ width: size }}>
|
||||
<label
|
||||
className='mt-2 btn btn-primary text-center cursor-pointer text-xs'
|
||||
htmlFor='single'>
|
||||
{uploading ? "Uploading ..." : "Update my avatar"}
|
||||
</label>
|
||||
<input
|
||||
style={{
|
||||
visibility: "hidden",
|
||||
position: "absolute",
|
||||
}}
|
||||
type='file'
|
||||
id='single'
|
||||
accept='image/*'
|
||||
onChange={uploadAvatar}
|
||||
disabled={uploading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Avatar;
|
||||
@@ -46,7 +46,7 @@ const Contact = () => {
|
||||
<div className='font-main container'>
|
||||
<div className=' m-auto max-w-lg'>
|
||||
<div className='flex justify-center'>
|
||||
<h2 className='text-3xl sm:text-5xl text-center mb-5 mt-0 font-bold'>
|
||||
<h2 className='text-3xl sm:text-5xl text-center mb-5 mt-0 font-bold font-title'>
|
||||
Contact
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,8 @@ function with your new elements.
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import Avatar from "./Avatar";
|
||||
import MyModal from "./MyModal";
|
||||
import { supabase } from "../utils/supabaseClient";
|
||||
|
||||
export default function Account({ session }) {
|
||||
@@ -74,12 +76,30 @@ export default function Account({ session }) {
|
||||
|
||||
return (
|
||||
<div className='form-widget mt-10 flex flex-col text-left'>
|
||||
<div className="mb-5 flex">
|
||||
<label htmlFor='email' className="my-auto">Email</label>
|
||||
<input className="input input-primary input-bordered input-sm ml-2 flex-1" id='email' type='text' value={session.user.email} disabled />
|
||||
<Avatar
|
||||
url={avatar_url}
|
||||
size={150}
|
||||
onUpload={(url) => {
|
||||
setAvatarUrl(url);
|
||||
updateProfile({ username, website, avatar_url: url });
|
||||
}}
|
||||
/>
|
||||
<div className='mb-5 flex'>
|
||||
<label htmlFor='email' className='my-auto'>
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm ml-2 flex-1'
|
||||
id='email'
|
||||
type='text'
|
||||
value={session.user.email}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5 flex">
|
||||
<label htmlFor='username' className="my-auto">Name</label>
|
||||
<div className='mb-5 flex'>
|
||||
<label htmlFor='username' className='my-auto'>
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm flex-1 ml-2'
|
||||
id='username'
|
||||
@@ -88,8 +108,10 @@ export default function Account({ session }) {
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5 flex">
|
||||
<label htmlFor='website' className="my-auto">Website</label>
|
||||
<div className='mb-5 flex'>
|
||||
<label htmlFor='website' className='my-auto'>
|
||||
Website
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm flex-1 ml-2'
|
||||
id='website'
|
||||
@@ -99,7 +121,7 @@ export default function Account({ session }) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="m-auto">
|
||||
<div className='m-auto'>
|
||||
<button
|
||||
className='btn btn-primary btn-sm'
|
||||
onClick={() => updateProfile({ username, website, avatar_url })}
|
||||
|
||||
@@ -56,7 +56,7 @@ const Layout = (props) => {
|
||||
<meta name='theme-color' content='#ffffff' />
|
||||
</Head>
|
||||
<Nav user={user} />
|
||||
<main className='flex flex-col items-center justify-center w-full flex-1 p-2 text-center'>
|
||||
<main className='flex flex-col items-center justify-center w-full flex-1 p-2 text-center font-body'>
|
||||
{props.children}
|
||||
</main>
|
||||
<ToastContainer
|
||||
|
||||
92
components/MyModal.js
Normal file
92
components/MyModal.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
|
||||
export default function MyModal() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
function closeModal() {
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
setIsOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className=" inset-0 flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={openModal}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-black rounded-md bg-opacity-20 hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
|
||||
>
|
||||
Open dialog
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="fixed inset-0 z-10 overflow-y-auto"
|
||||
onClose={closeModal}
|
||||
>
|
||||
<div className="min-h-screen px-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0" />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span
|
||||
className="inline-block h-screen align-middle"
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-2xl">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900"
|
||||
>
|
||||
Payment successful
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Your payment has been successfully submitted. We’ve sent
|
||||
your an email with all of the details of your order.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center px-4 py-2 text-sm font-medium text-blue-900 bg-blue-100 border border-transparent rounded-md hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
|
||||
onClick={closeModal}
|
||||
>
|
||||
Got it, thanks!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -39,6 +39,10 @@ const Nav = (props) => {
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Link href='/pricing'>
|
||||
<a className='btn btn-ghost btn-sm'>Pricing</a>
|
||||
</Link>
|
||||
|
||||
<Link href='/'>
|
||||
<a className='btn btn-ghost btn-sm'>About</a>
|
||||
</Link>
|
||||
@@ -62,7 +66,7 @@ const Nav = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<nav className='navbar mb-2 w-full'>
|
||||
<nav className='navbar mb-2 w-full px-5'>
|
||||
<Link href='/'>
|
||||
<a>
|
||||
<Image src={Logo} />
|
||||
|
||||
136
components/Pricing.js
Normal file
136
components/Pricing.js
Normal file
@@ -0,0 +1,136 @@
|
||||
import { Switch } from "@headlessui/react";
|
||||
import { useState } from "react";
|
||||
|
||||
const Pricing = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const pricing = {
|
||||
monthly: {
|
||||
personal: "$5/mo",
|
||||
team: "15/mo",
|
||||
pro: "35/mo",
|
||||
},
|
||||
yearly: {
|
||||
personal: "$50/yr",
|
||||
team: "$150/yr",
|
||||
pro: "$350/yr",
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div className='w-full mx-auto px-5 py-10 mb-10'>
|
||||
<div className='text-center max-w-xl mx-auto'>
|
||||
<h1 className='text-3xl sm:text-5xl font-bold font-title mb-5'>Pricing</h1>
|
||||
<h3 className='text-lg font-light leading-8 p-3 mb-5'>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit repellat
|
||||
dignissimos laboriosam odit accusamus porro
|
||||
</h3>
|
||||
</div>
|
||||
<div className='flex justify-between max-w-xs m-auto mb-3'>
|
||||
<div>
|
||||
<p className={`${enabled ? "text-gray-500" : null}`}>
|
||||
Billed monthly
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onChange={setEnabled}
|
||||
className={`bg-primary relative inline-flex flex-shrink-0 h-[38px] w-[74px]
|
||||
border-2 border-transparent rounded-full cursor-pointer transition-colors
|
||||
ease-in-out duration-200 focus:outline-none focus-visible:ring-2
|
||||
focus-visible:ring-white focus-visible:ring-opacity-75`}>
|
||||
<span className='sr-only'>Switch bill</span>
|
||||
<span
|
||||
aria-hidden='true'
|
||||
className={`${enabled ? "translate-x-9" : "translate-x-0"}
|
||||
pointer-events-none inline-block h-[34px] w-[34px] rounded-full bg-white shadow-lg transform ring-0 transition ease-in-out duration-200`}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
<div>
|
||||
<p className={`${!enabled ? "text-gray-500" : null}`}>
|
||||
Billed annually
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='max-w-4xl mx-auto md:flex'>
|
||||
<div className='w-full md:w-1/3 md:max-w-none bg-white px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Personal</h2>
|
||||
<h3 className='text-center font-bold text-4xl mb-5'>
|
||||
{enabled ? pricing.yearly.personal : pricing.monthly.personal}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Lorem ipsum
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Dolor sit amet
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<button className='btn btn-primary w-full'>Buy Now</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full md:w-1/3 md:max-w-none bg-white px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:-mx-3 md:mb-0 rounded-md shadow-lg shadow-gray-600 md:relative md:z-50 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Team</h2>
|
||||
<h3 className='text-center font-bold text-4xl md:text-5xl mb-5'>
|
||||
{enabled ? pricing.yearly.team : pricing.monthly.team}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Lorem ipsum
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Dolor sit amet
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Consectetur
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Adipisicing
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Elit repellat
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<button className='btn btn-primary w-full'>Buy Now</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full md:w-1/3 md:max-w-none bg-white px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Pro</h2>
|
||||
<h3 className='text-center font-bold text-4xl mb-5'>
|
||||
{enabled ? pricing.yearly.pro : pricing.monthly.pro}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Lorem ipsum
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Dolor sit amet
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Consectetur
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Adipisicing
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Much more...
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<button className='btn btn-primary w-full'>Buy Now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pricing;
|
||||
924
package-lock.json
generated
924
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@sendgrid/mail": "^7.4.4",
|
||||
"@supabase/supabase-js": "^1.15.0",
|
||||
"@supabase/ui": "^0.27.3",
|
||||
@@ -23,6 +24,6 @@
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.0.4",
|
||||
"postcss": "^8.1.10",
|
||||
"tailwindcss": "^2.1.1"
|
||||
"tailwindcss": "^2.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import "tailwindcss/tailwind.css";
|
||||
import "./global.css";
|
||||
|
||||
import { Auth } from "@supabase/ui";
|
||||
import { DefaultSeo } from "next-seo";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Auth } from "@supabase/ui";
|
||||
import AuthComponent from "../components/Auth";
|
||||
import AuthText from "components/AuthText";
|
||||
import Layout from "components/Layout";
|
||||
import { NextSeo } from "next-seo";
|
||||
import { useEffect } from "react";
|
||||
@@ -23,15 +24,9 @@ const AuthPage = () => {
|
||||
/>
|
||||
|
||||
<Layout>
|
||||
<div>
|
||||
<h1 className='text-4xl font-bold md:text-5xl'>Auth</h1>
|
||||
<div className='container'>
|
||||
{!session && (
|
||||
<div className='max-w-md'>
|
||||
<AuthComponent />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex flex-wrap justify-evenly w-full'>
|
||||
<AuthText />
|
||||
<div>{!session && <AuthComponent />}</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
|
||||
@@ -34,7 +34,7 @@ const DashboardPage = ({ user }) => {
|
||||
|
||||
<Layout>
|
||||
<div>
|
||||
<h1 className='text-4xl font-bold md:text-5xl'>Dashboard</h1>
|
||||
<h1 className='text-4xl font-bold md:text-5xl font-title'>Dashboard</h1>
|
||||
<>
|
||||
{!session ? (
|
||||
<div className='max-w-md'>
|
||||
|
||||
2
pages/global.css
Normal file
2
pages/global.css
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss/tailwind.css";
|
||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&family=Poppins:wght@400;600;800&display=swap");
|
||||
@@ -9,7 +9,7 @@ const Home = () => {
|
||||
description={`SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS`}
|
||||
/>
|
||||
<Layout>
|
||||
<h2 className='text-5xl md:text-6xl font-bold'>
|
||||
<h2 className='text-5xl md:text-6xl font-bold font-title'>
|
||||
SupaNexTail <span className='text-blue-600'>Boilerplate</span>
|
||||
</h2>
|
||||
</Layout>
|
||||
|
||||
18
pages/pricing.js
Normal file
18
pages/pricing.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import Layout from "components/Layout";
|
||||
import { NextSeo } from "next-seo";
|
||||
import Pricing from "components/Pricing";
|
||||
|
||||
const PricingPage = () => {
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}
|
||||
description={`SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS`}
|
||||
/>
|
||||
<Layout>
|
||||
<Pricing />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PricingPage;
|
||||
BIN
public/auth.png
Normal file
BIN
public/auth.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -3,6 +3,10 @@ module.exports = {
|
||||
purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
fontFamily: {
|
||||
title: ["Poppins"],
|
||||
body: ["Open Sans"],
|
||||
},
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
|
||||
Reference in New Issue
Block a user