mirror of
https://github.com/fergalmoran/supanextail.git
synced 2025-12-22 09:17:54 +00:00
First pass prettier/eslint and headwind
This commit is contained in:
@@ -8,8 +8,7 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"next",
|
"next",
|
||||||
"next/core-web-vitals",
|
"next/core-web-vitals",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
||||||
"prettier"
|
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
@@ -30,9 +29,18 @@
|
|||||||
"prettier/prettier": [
|
"prettier/prettier": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"endOfLine": "auto"
|
"semi": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"printWidth": 80,
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"bracketSpacing": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"react/prop-types": "off",
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
"react/jsx-filename-extension": [
|
"react/jsx-filename-extension": [
|
||||||
2,
|
2,
|
||||||
@@ -44,6 +52,15 @@
|
|||||||
".tsx"
|
".tsx"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"allowExpressions": true,
|
||||||
|
"allowTypedFunctionExpressions": true,
|
||||||
|
"allowHigherOrderFunctions": true,
|
||||||
|
"allowConciseArrowFunctionExpressionsStartingWithVoid": true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"useTabs": true,
|
"useTabs": false,
|
||||||
"endOfLine": "auto",
|
"endOfLine": "auto",
|
||||||
"printWidth": 100
|
"printWidth": 80,
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"bracketSpacing": true
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@ import Image from 'next/image';
|
|||||||
import authImage from 'public/auth.png';
|
import authImage from 'public/auth.png';
|
||||||
|
|
||||||
const AuthText = (): JSX.Element => (
|
const AuthText = (): JSX.Element => (
|
||||||
<div className="lg:mt-0 max-w-lg flex flex-col text-xl">
|
<div className="flex flex-col max-w-lg text-xl lg:mt-0">
|
||||||
<div className="mt-10 mb-3 m-auto">
|
<div className="m-auto mt-10 mb-3">
|
||||||
<Image
|
<Image
|
||||||
src={authImage}
|
src={authImage}
|
||||||
width={authImage.width / 1.5}
|
width={authImage.width / 1.5}
|
||||||
@@ -11,12 +11,12 @@ const AuthText = (): JSX.Element => (
|
|||||||
alt="A rocketship"
|
alt="A rocketship"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl font-title font-semibold text-center">
|
<h2 className="text-4xl font-semibold text-center font-title">
|
||||||
Join SupaNexTail for <span className="text-primary">free</span>!
|
Join SupaNexTail for <span className="text-primary">free</span>!
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mb-5 mt-8 leading-9">
|
<p className="mt-8 mb-5 leading-9">
|
||||||
Create your website in a few minutes with our boilerplate. You can use the login system, this
|
Create your website in a few minutes with our boilerplate. You can use the
|
||||||
will allow you to discover the sample dashboard page.
|
login system, this will allow you to discover the sample dashboard page.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
|||||||
|
|
||||||
async function downloadImage(path: string) {
|
async function downloadImage(path: string) {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase.storage.from('avatars').download(path);
|
const { data, error } = await supabase.storage
|
||||||
|
.from('avatars')
|
||||||
|
.download(path);
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,9 @@ const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error: uploadError } = await supabase.storage.from('avatars').upload(filePath, file);
|
const { error: uploadError } = await supabase.storage
|
||||||
|
.from('avatars')
|
||||||
|
.upload(filePath, file);
|
||||||
|
|
||||||
if (uploadError) {
|
if (uploadError) {
|
||||||
throw uploadError;
|
throw uploadError;
|
||||||
@@ -83,24 +87,23 @@ const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<div className="m-auto mb-5">
|
<div className="m-auto mb-5">
|
||||||
{avatarUrl ? (
|
{avatarUrl ? (
|
||||||
<div className="w-full flex justify-center">
|
<div className="flex justify-center w-full">
|
||||||
<Image
|
<Image
|
||||||
loader={customImgLoader} //Using custom loader because of this issue https://github.com/vercel/next.js/discussions/19732
|
loader={customImgLoader} //Using custom loader because of this issue https://github.com/vercel/next.js/discussions/19732
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
height={100}
|
height={100}
|
||||||
width={100}
|
width={100}
|
||||||
alt="Avatar"
|
alt="Avatar"
|
||||||
className="avatar rounded-full w-28 h-28"
|
className="rounded-full w-28 h-28 avatar"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="avatar rounded-full w-28 h-28" />
|
<div className="rounded-full w-28 h-28 avatar" />
|
||||||
)}
|
)}
|
||||||
<div style={{ width: size }}>
|
<div style={{ width: size }}>
|
||||||
<label
|
<label
|
||||||
className="mt-2 btn btn-primary text-center cursor-pointer text-xs btn-sm"
|
className="mt-2 text-xs text-center cursor-pointer btn btn-primary btn-sm"
|
||||||
htmlFor="single"
|
htmlFor="single">
|
||||||
>
|
|
||||||
{uploading ? 'Uploading ...' : 'Update my avatar'}
|
{uploading ? 'Uploading ...' : 'Update my avatar'}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import cardTheme from 'public/landing/theme.svg';
|
|||||||
|
|
||||||
const CardsLanding = (): JSX.Element => (
|
const CardsLanding = (): JSX.Element => (
|
||||||
<div className="mt-14">
|
<div className="mt-14">
|
||||||
<h2 className="uppercase font-bold text-4xl tracking-wide text-center">
|
<h2 className="text-4xl font-bold tracking-wide text-center uppercase">
|
||||||
We've got you covered
|
We've got you covered
|
||||||
</h2>
|
</h2>
|
||||||
<p className="max-w-md m-auto text-center">
|
<p className="max-w-md m-auto text-center">
|
||||||
Don’t waste your time and reinvent the wheel, we have provided you with a maximum of features
|
Don’t waste your time and reinvent the wheel, we have provided you with a
|
||||||
so that you only have one goal, to make your SaaS a reality.
|
maximum of features so that you only have one goal, to make your SaaS a
|
||||||
|
reality.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap justify-center mt-10">
|
<div className="flex flex-wrap justify-center mt-10">
|
||||||
<CardLanding
|
<CardLanding
|
||||||
@@ -27,7 +28,11 @@ const CardsLanding = (): JSX.Element => (
|
|||||||
text="Integrated backend already setup with Next.js API Routes"
|
text="Integrated backend already setup with Next.js API Routes"
|
||||||
title="Backend"
|
title="Backend"
|
||||||
/>
|
/>
|
||||||
<CardLanding image={cardAuth} text="Auth and user management with Supabase" title="Auth" />
|
<CardLanding
|
||||||
|
image={cardAuth}
|
||||||
|
text="Auth and user management with Supabase"
|
||||||
|
title="Auth"
|
||||||
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardResponsive}
|
image={cardResponsive}
|
||||||
text="Mobile ready, fully responsive and customizable with Tailwind CSS"
|
text="Mobile ready, fully responsive and customizable with Tailwind CSS"
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ const Contact = (): JSX.Element => {
|
|||||||
const sendEmail = () => {
|
const sendEmail = () => {
|
||||||
const name = (document.getElementById('name') as HTMLInputElement).value;
|
const name = (document.getElementById('name') as HTMLInputElement).value;
|
||||||
const email = (document.getElementById('email') as HTMLInputElement).value;
|
const email = (document.getElementById('email') as HTMLInputElement).value;
|
||||||
const message = (document.getElementById('message') as HTMLInputElement).value;
|
const message = (document.getElementById('message') as HTMLInputElement)
|
||||||
|
.value;
|
||||||
|
|
||||||
if (name && email && message) {
|
if (name && email && message) {
|
||||||
axios
|
axios
|
||||||
@@ -42,21 +43,21 @@ const Contact = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="max-w-xl m-auto px-5 py-10">
|
<div className="max-w-xl px-5 py-10 m-auto">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<h2 className="text-3xl sm:text-4xl text-center mb-5 mt-0 font-bold font-title">
|
<h2 className="mt-0 mb-5 text-3xl font-bold text-center sm:text-4xl font-title">
|
||||||
Contact
|
Contact
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="m-auto text-center">
|
<p className="m-auto text-center">
|
||||||
Do you have a question about SupaNexTail? A cool feature you'd like us to integrate? A bug
|
Do you have a question about SupaNexTail? A cool feature you'd like us
|
||||||
to report? Don't hesitate!
|
to integrate? A bug to report? Don't hesitate!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<form className="m-auto mt-5 grid grid-cols-1 gap-4 md:grid-cols-2 p-5">
|
<form className="grid grid-cols-1 gap-4 p-5 m-auto mt-5 md:grid-cols-2">
|
||||||
<div className="flex flex-col max-w-xs">
|
<div className="flex flex-col max-w-xs">
|
||||||
<p className="font-light mb-4 text-left">Your Name</p>
|
<p className="mb-4 font-light text-left">Your Name</p>
|
||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
@@ -65,7 +66,7 @@ const Contact = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col max-w-xs mb-3">
|
<div className="flex flex-col max-w-xs mb-3">
|
||||||
<p className="font-light mb-4 text-left">Your email</p>
|
<p className="mb-4 font-light text-left">Your email</p>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
@@ -74,13 +75,13 @@ const Contact = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col col-span-full w-fulll">
|
<div className="flex flex-col col-span-full w-fulll">
|
||||||
<p className="font-light mb-4 text-left">Message</p>
|
<p className="mb-4 font-light text-left">Message</p>
|
||||||
<textarea
|
<textarea
|
||||||
id="message"
|
id="message"
|
||||||
name="message"
|
name="message"
|
||||||
placeholder="Enter your message here..."
|
placeholder="Enter your message here..."
|
||||||
rows={5}
|
rows={5}
|
||||||
className="input input-primary input-bordered resize-none w-full h-32 pt-2"
|
className="w-full h-32 pt-2 resize-none input input-primary input-bordered"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -89,8 +90,7 @@ const Contact = (): JSX.Element => {
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
sendEmail();
|
sendEmail();
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
Submit{' '}
|
Submit{' '}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ type DashboardProps = {
|
|||||||
planName: string;
|
planName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Dashboard = ({ profile, session, planName }: DashboardProps): JSX.Element => {
|
const Dashboard = ({
|
||||||
|
profile,
|
||||||
|
session,
|
||||||
|
planName,
|
||||||
|
}: DashboardProps): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [username, setUsername] = useState(profile?.username || '');
|
const [username, setUsername] = useState(profile?.username || '');
|
||||||
@@ -75,9 +79,11 @@ const Dashboard = ({ profile, session, planName }: DashboardProps): JSX.Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col text-left w-full max-w-xl m-auto px-5 py-10">
|
<div className="flex flex-col w-full max-w-xl px-5 py-10 m-auto text-left">
|
||||||
<div className="max-w-sm flex flex-col justify-center m-auto w-full p-5">
|
<div className="flex flex-col justify-center w-full max-w-sm p-5 m-auto">
|
||||||
<h1 className="text-4xl font-bold md:text-5xl font-title text-center mb-10">Dashboard</h1>
|
<h1 className="mb-10 text-4xl font-bold text-center md:text-5xl font-title">
|
||||||
|
Dashboard
|
||||||
|
</h1>
|
||||||
<Avatar
|
<Avatar
|
||||||
url={avatar_url}
|
url={avatar_url}
|
||||||
size={150}
|
size={150}
|
||||||
@@ -86,36 +92,36 @@ const Dashboard = ({ profile, session, planName }: DashboardProps): JSX.Element
|
|||||||
updateProfile({ username, website, avatar_url: url });
|
updateProfile({ username, website, avatar_url: url });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="mb-5 flex flex-col">
|
<div className="flex flex-col mb-5">
|
||||||
<label htmlFor="email" className="my-auto text-sm mb-2">
|
<label htmlFor="email" className="my-auto mb-2 text-sm">
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="input input-primary input-bordered input-sm flex-1 text-base-100"
|
className="flex-1 input input-primary input-bordered input-sm text-base-100"
|
||||||
id="email"
|
id="email"
|
||||||
type="text"
|
type="text"
|
||||||
value={session.user?.email}
|
value={session.user?.email}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-5 flex flex-col">
|
<div className="flex flex-col mb-5">
|
||||||
<label htmlFor="username" className="my-auto text-sm mb-2">
|
<label htmlFor="username" className="my-auto mb-2 text-sm">
|
||||||
Name
|
Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="input input-primary input-bordered input-sm flex-1"
|
className="flex-1 input input-primary input-bordered input-sm"
|
||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
value={username || ''}
|
value={username || ''}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-5 flex flex-col">
|
<div className="flex flex-col mb-5">
|
||||||
<label htmlFor="website" className="my-auto text-sm mb-2">
|
<label htmlFor="website" className="my-auto mb-2 text-sm">
|
||||||
Website
|
Website
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="input input-primary input-bordered input-sm flex-1"
|
className="flex-1 input input-primary input-bordered input-sm"
|
||||||
id="website"
|
id="website"
|
||||||
type="website"
|
type="website"
|
||||||
value={website || ''}
|
value={website || ''}
|
||||||
@@ -127,14 +133,13 @@ const Dashboard = ({ profile, session, planName }: DashboardProps): JSX.Element
|
|||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-sm"
|
className="btn btn-primary btn-sm"
|
||||||
onClick={() => updateProfile({ username, website, avatar_url })}
|
onClick={() => updateProfile({ username, website, avatar_url })}
|
||||||
disabled={loading}
|
disabled={loading}>
|
||||||
>
|
|
||||||
{loading ? 'Loading ...' : 'Update My Profile'}
|
{loading ? 'Loading ...' : 'Update My Profile'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-w-xl flex flex-row flex-wrap m-auto w-full p-5 bordered border-2 border-primary shadow-lg my-5">
|
<div className="flex flex-row flex-wrap w-full max-w-xl p-5 m-auto my-5 border-2 shadow-lg bordered border-primary">
|
||||||
<Image src={Plan} alt="credit card" />
|
<Image src={Plan} alt="credit card" />
|
||||||
<div className="flex flex-col m-auto">
|
<div className="flex flex-col m-auto">
|
||||||
<h2>Your current plan</h2>
|
<h2>Your current plan</h2>
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ const Footer = (): JSX.Element => {
|
|||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<footer className="w-full flex">
|
<footer className="flex w-full">
|
||||||
<nav className=" mr-auto">
|
<nav className="mr-auto">
|
||||||
<div className="flex flex-col sm:flex-row justify-evenly w-full sm:space-x-10">
|
<div className="flex flex-col w-full justify-evenly sm:flex-row sm:space-x-10">
|
||||||
<div className="">© {process.env.NEXT_PUBLIC_TITLE}</div>
|
<div className="">© {process.env.NEXT_PUBLIC_TITLE}</div>
|
||||||
<Link href="/privacy">
|
<Link href="/privacy">
|
||||||
<a>Privacy Policy</a>
|
<a>Privacy Policy</a>
|
||||||
@@ -18,7 +18,7 @@ const Footer = (): JSX.Element => {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div className="mr-5 my-auto">
|
<div className="my-auto mr-5">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -6,52 +6,60 @@ import start from 'public/landing/start.svg';
|
|||||||
import supabaseImage from 'public/landing/supabase.svg';
|
import supabaseImage from 'public/landing/supabase.svg';
|
||||||
|
|
||||||
const Landing = (): JSX.Element => (
|
const Landing = (): JSX.Element => (
|
||||||
<div className="mt-10 mb-20 text-base-content w-full">
|
<div className="w-full mt-10 mb-20 text-base-content">
|
||||||
<div className="flex max-w-6xl m-auto justify-around flex-wrap">
|
<div className="flex flex-wrap justify-around max-w-6xl m-auto">
|
||||||
<div className="max-w-sm mr-16 my-auto">
|
<div className="max-w-sm my-auto mr-16">
|
||||||
<h2 className="text-4xl font-bold font-title text-center lg:text-left leading-normal">
|
<h2 className="text-4xl font-bold leading-normal text-center lg:text-left font-title">
|
||||||
Build your <span className="text-primary">SaaS</span> in the blink of an eye!
|
Build your <span className="text-primary">SaaS</span> in the blink of
|
||||||
|
an eye!
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-center lg:text-left">
|
<p className="text-center lg:text-left">
|
||||||
SupaNexTail got your back, and takes care of the initial setup, sometimes time consuming,
|
SupaNexTail got your back, and takes care of the initial setup,
|
||||||
but essential to your success.
|
sometimes time consuming, but essential to your success.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
<Image src={landTop} height={417} width={583} alt="Construction of a website" />
|
<Image
|
||||||
|
src={landTop}
|
||||||
|
height={417}
|
||||||
|
width={583}
|
||||||
|
alt="Construction of a website"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CardsLanding />
|
<CardsLanding />
|
||||||
<div className="flex max-w-6xl m-auto justify-around mt-14 flex-wrap">
|
<div className="flex flex-wrap justify-around max-w-6xl m-auto mt-14">
|
||||||
<div className="max-w-sm mr-16 my-auto">
|
<div className="max-w-sm my-auto mr-16">
|
||||||
<h2 className="text-4xl font-bold font-title text-left leading-normal">
|
<h2 className="text-4xl font-bold leading-normal text-left font-title">
|
||||||
All you need to start <span className="text-primary">now</span>
|
All you need to start <span className="text-primary">now</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
SupaNexTail got your back, and takes care of the initial setup, sometimes time consuming,
|
SupaNexTail got your back, and takes care of the initial setup,
|
||||||
but essential to your success.
|
sometimes time consuming, but essential to your success.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
<Image src={start} alt="screenshot of the website" />
|
<Image src={start} alt="screenshot of the website" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex max-w-6xl m-auto justify-around mt-24 flex-wrap">
|
<div className="flex flex-wrap justify-around max-w-6xl m-auto mt-24">
|
||||||
<div className="max-w-md my-auto order-1 lg:order-2">
|
<div className="order-1 max-w-md my-auto lg:order-2">
|
||||||
<h2 className="text-4xl font-bold font-title text-left leading-normal">
|
<h2 className="text-4xl font-bold leading-normal text-left font-title">
|
||||||
Leverage the power of <span className="text-primary">Supabase</span>
|
Leverage the power of <span className="text-primary">Supabase</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Supabase is an open source Firebase alternative. You’ll have a database, an auth system, a
|
Supabase is an open source Firebase alternative. You’ll have a
|
||||||
storage system, and much more in one product.
|
database, an auth system, a storage system, and much more in one
|
||||||
|
product.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
SupaNexTail uses Supabase at its core, and preconfigures all the useful elements for your
|
SupaNexTail uses Supabase at its core, and preconfigures all the
|
||||||
site. User registration, synchronization with Stripe, we’ve got you covered!
|
useful elements for your site. User registration, synchronization with
|
||||||
|
Stripe, we’ve got you covered!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-md order-2 lg:order-1 flex">
|
<div className="flex order-2 max-w-md lg:order-1">
|
||||||
<Image src={supabaseImage} alt="screenshot of the Supabase website" />
|
<Image src={supabaseImage} alt="screenshot of the Supabase website" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,17 +26,31 @@ const Layout = ({ children }: LayoutProps): JSX.Element => {
|
|||||||
const { user, signOut } = useAuth();
|
const { user, signOut } = useAuth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen w-full bg-base-100 text-base-content m-auto font-body">
|
<div className="w-full min-h-screen m-auto bg-base-100 text-base-content font-body">
|
||||||
<Head>
|
<Head>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
rel="apple-touch-icon"
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="max-w-7xl flex flex-col min-h-screen mx-auto p-5">
|
<div className="flex flex-col min-h-screen p-5 mx-auto max-w-7xl">
|
||||||
<Nav user={user} signOut={signOut} />
|
<Nav user={user} signOut={signOut} />
|
||||||
<main className="flex-1">{children}</main>
|
<main className="flex-1">{children}</main>
|
||||||
<ToastContainer position="bottom-center" />
|
<ToastContainer position="bottom-center" />
|
||||||
|
|||||||
@@ -48,30 +48,33 @@ const MailingList = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="my-10 mt-24 m-auto flex flex-col">
|
<div className="flex flex-col m-auto my-10 mt-24">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold font-title uppercase text-center">
|
<h2 className="text-3xl font-bold text-center uppercase md:text-4xl font-title">
|
||||||
Stay Tuned
|
Stay Tuned
|
||||||
</h2>
|
</h2>
|
||||||
<Image src={Mailing} alt="Mail" />
|
<Image src={Mailing} alt="Mail" />
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<p className="text-center max-w-md m-auto">
|
<p className="max-w-md m-auto text-center">
|
||||||
Want to be the first to know when SupaNexTail launches and get an exclusive discount? Sign
|
Want to be the first to know when SupaNexTail launches and get an
|
||||||
up for the newsletter!
|
exclusive discount? Sign up for the newsletter!
|
||||||
</p>
|
</p>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-5 m-auto">
|
<div className="m-auto mt-5">
|
||||||
<input
|
<input
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setMail(e.target.value);
|
setMail(e.target.value);
|
||||||
}}
|
}}
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Your email"
|
placeholder="Your email"
|
||||||
className={`input input-primary input-bordered ${valid ? null : 'input-error'}`}
|
className={`input input-primary input-bordered ${
|
||||||
|
valid ? null : 'input-error'
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={validateEmail}
|
onClick={validateEmail}
|
||||||
className={`btn ml-3 ${loading ? 'btn-disabled loading' : 'btn-primary'}`}
|
className={`btn ml-3 ${
|
||||||
>
|
loading ? 'btn-disabled loading' : 'btn-primary'
|
||||||
|
}`}>
|
||||||
I'm in!
|
I'm in!
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{user ? (
|
{user ? (
|
||||||
<button id="logOutBtn" className="btn btn-xs text-xs" onClick={() => signOut()}>
|
<button
|
||||||
|
id="logOutBtn"
|
||||||
|
className="text-xs btn btn-xs"
|
||||||
|
onClick={() => signOut()}>
|
||||||
<LogOut size={12} className="mr-2" />
|
<LogOut size={12} className="mr-2" />
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
@@ -48,7 +51,9 @@ const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
|||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/signup">
|
<Link href="/signup">
|
||||||
<a id="signup" className="btn btn-sm btn-primary font-body normal-case font-normal">
|
<a
|
||||||
|
id="signup"
|
||||||
|
className="font-normal normal-case btn btn-sm btn-primary font-body">
|
||||||
Sign Up
|
Sign Up
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -58,14 +63,14 @@ const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar mb-2 w-full">
|
<nav className="w-full mb-2 navbar">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a>
|
<a>
|
||||||
<Image src={Logo} alt="SupaNexTail Logo" />
|
<Image src={Logo} alt="SupaNexTail Logo" />
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="hidden lg:flex text-center flex-col lg:flex-row lg:space-x-10 font-body text-sm ml-auto">
|
<div className="flex-col hidden ml-auto text-sm text-center lg:flex lg:flex-row lg:space-x-10 font-body">
|
||||||
{NavMenu}
|
{NavMenu}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-auto lg:hidden">
|
<div className="ml-auto lg:hidden">
|
||||||
@@ -73,7 +78,9 @@ const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
|||||||
<div tabIndex={0} className="m-1 cursor-pointer">
|
<div tabIndex={0} className="m-1 cursor-pointer">
|
||||||
<Menu />
|
<Menu />
|
||||||
</div>
|
</div>
|
||||||
<div className="menu dropdown-content mt-3 text-center space-y-3 w-24">{NavMenu}</div>
|
<div className="w-24 mt-3 space-y-3 text-center menu dropdown-content">
|
||||||
|
{NavMenu}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ const PaymentModal = ({ open, setPayment }: PaymentModalProps): JSX.Element => {
|
|||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="fixed inset-0 z-10 overflow-y-auto bg-gray-500 bg-opacity-50"
|
className="fixed inset-0 z-10 overflow-y-auto bg-gray-500 bg-opacity-50"
|
||||||
onClose={closeModal}
|
onClose={closeModal}>
|
||||||
>
|
|
||||||
<div className="min-h-screen px-4 text-center">
|
<div className="min-h-screen px-4 text-center">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
@@ -28,13 +27,14 @@ const PaymentModal = ({ open, setPayment }: PaymentModalProps): JSX.Element => {
|
|||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
leave="ease-in duration-200"
|
leave="ease-in duration-200"
|
||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0">
|
||||||
>
|
|
||||||
<Dialog.Overlay className="fixed inset-0" />
|
<Dialog.Overlay className="fixed inset-0" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
{/* This element is to trick the browser into centering the modal contents. */}
|
{/* This element is to trick the browser into centering the modal contents. */}
|
||||||
<span className="inline-block h-screen align-middle" aria-hidden="true">
|
<span
|
||||||
|
className="inline-block h-screen align-middle"
|
||||||
|
aria-hidden="true">
|
||||||
​
|
​
|
||||||
</span>
|
</span>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
@@ -44,18 +44,25 @@ const PaymentModal = ({ open, setPayment }: PaymentModalProps): JSX.Element => {
|
|||||||
enterTo="opacity-100 scale-100"
|
enterTo="opacity-100 scale-100"
|
||||||
leave="ease-in duration-200"
|
leave="ease-in duration-200"
|
||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95">
|
||||||
>
|
<div className="inline-block w-full max-w-lg p-8 my-8 overflow-hidden text-left align-middle transition-all transform border-2 shadow-xl rounded-2xl bg-base-100 text-base-content border-accent-focus">
|
||||||
<div className="inline-block w-full max-w-lg p-8 my-8 overflow-hidden text-left align-middle transition-all transform shadow-xl rounded-2xl bg-base-100 text-base-content border-2 border-accent-focus">
|
<Dialog.Title
|
||||||
<Dialog.Title as="h3" className="text-2xl font-bold leading-6 mb-5 text-center">
|
as="h3"
|
||||||
|
className="mb-5 text-2xl font-bold leading-6 text-center">
|
||||||
Payment successful 🎉
|
Payment successful 🎉
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p>Your payment has been successfully submitted. Thank you for your support!</p>
|
<p>
|
||||||
|
Your payment has been successfully submitted. Thank you for
|
||||||
|
your support!
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<button type="button" className="btn btn-accent flex m-auto" onClick={closeModal}>
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex m-auto btn btn-accent"
|
||||||
|
onClick={closeModal}>
|
||||||
Got it, thanks!
|
Got it, thanks!
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ const Pricing = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLButtonElement>, priceId: string) => {
|
const handleSubmit = async (
|
||||||
|
e: React.SyntheticEvent<HTMLButtonElement>,
|
||||||
|
priceId: string
|
||||||
|
) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Create a Checkout Session. This will redirect the user to the Stripe website for the payment.
|
// Create a Checkout Session. This will redirect the user to the Stripe website for the payment.
|
||||||
if (sub) {
|
if (sub) {
|
||||||
@@ -58,11 +61,11 @@ const Pricing = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="container px-6 py-8 mx-auto text-base-100">
|
<div className="container px-6 py-8 mx-auto text-base-100">
|
||||||
<h2 className="text-3xl sm:text-4xl text-center mb-5 mt-0 font-bold font-title text-base-content">
|
<h2 className="mt-0 mb-5 text-3xl font-bold text-center sm:text-4xl font-title text-base-content">
|
||||||
Pricing
|
Pricing
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-16 flex flex-col items-center justify-center space-y-8 lg:-mx-4 lg:flex-row lg:items-stretch lg:space-y-0">
|
<div className="flex flex-col items-center justify-center mt-16 space-y-8 lg:flex-row lg:items-stretch lg:-mx-4 lg:space-y-0">
|
||||||
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center bg-base-100 rounded-lg lg:mx-4 shadow-lg text-base-content">
|
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center rounded-lg shadow-lg lg:mx-4 bg-base-100 text-base-content">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
||||||
Casual
|
Casual
|
||||||
@@ -80,7 +83,7 @@ const Pricing = (): JSX.Element => {
|
|||||||
<button className="btn btn-primary">Start for free</button>
|
<button className="btn btn-primary">Start for free</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center bg-base-100 rounded-lg lg:mx-4 shadow-lg text-base-content">
|
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center rounded-lg shadow-lg lg:mx-4 bg-base-100 text-base-content">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
||||||
Professional
|
Professional
|
||||||
@@ -101,18 +104,19 @@ const Pricing = (): JSX.Element => {
|
|||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
{sub ? 'Handle subscription' : 'Subscribe'}
|
{sub ? 'Handle subscription' : 'Subscribe'}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button className="btn btn-primary" onClick={() => router.push('/login')}>
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => router.push('/login')}>
|
||||||
Log in
|
Log in
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center bg-base-100 rounded-lg lg:mx-4 shadow-lg text-base-content">
|
<div className="flex flex-col w-full max-w-sm p-8 space-y-8 text-center rounded-lg shadow-lg lg:mx-4 bg-base-100 text-base-content">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
<h3 className="inline-flex items-center badge-neutral badge badge-lg bg-base-content text-base-100">
|
||||||
Business
|
Business
|
||||||
@@ -134,12 +138,13 @@ const Pricing = (): JSX.Element => {
|
|||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
{sub ? 'Handle subscription' : 'Subscribe'}
|
{sub ? 'Handle subscription' : 'Subscribe'}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button className="btn btn-primary" onClick={() => router.push('/login')}>
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => router.push('/login')}>
|
||||||
Log in
|
Log in
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,52 +1,66 @@
|
|||||||
const PrivacyPolicy = (): JSX.Element => (
|
const PrivacyPolicy = (): JSX.Element => (
|
||||||
<div className="max-w-xl text-left m-auto py-10">
|
<div className="max-w-xl py-10 m-auto text-left">
|
||||||
<h1 className="text-center">Privacy Policy for {process.env.NEXT_PUBLIC_TITLE}</h1>
|
<h1 className="text-center">
|
||||||
|
Privacy Policy for {process.env.NEXT_PUBLIC_TITLE}
|
||||||
|
</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
At {process.env.NEXT_PUBLIC_TITLE}, accessible from https://www.supanextail.dev, one of our
|
At {process.env.NEXT_PUBLIC_TITLE}, accessible from
|
||||||
main priorities is the privacy of our visitors. This Privacy Policy document contains types of
|
https://www.supanextail.dev, one of our main priorities is the privacy of
|
||||||
information that is collected and recorded by
|
our visitors. This Privacy Policy document contains types of information
|
||||||
|
that is collected and recorded by
|
||||||
{process.env.NEXT_PUBLIC_TITLE} and how we use it.
|
{process.env.NEXT_PUBLIC_TITLE} and how we use it.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you have additional questions or require more information about our Privacy Policy, do not
|
If you have additional questions or require more information about our
|
||||||
hesitate to contact us.
|
Privacy Policy, do not hesitate to contact us.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>General Data Protection Regulation (GDPR)</h2>
|
<h2>General Data Protection Regulation (GDPR)</h2>
|
||||||
<p>We are a Data Controller of your information.</p>
|
<p>We are a Data Controller of your information.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{process.env.NEXT_PUBLIC_TITLE} legal basis for collecting and using the personal information
|
{process.env.NEXT_PUBLIC_TITLE} legal basis for collecting and using the
|
||||||
described in this Privacy Policy depends on the Personal Information we collect and the
|
personal information described in this Privacy Policy depends on the
|
||||||
specific context in which we collect the information:
|
Personal Information we collect and the specific context in which we
|
||||||
|
collect the information:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{process.env.NEXT_PUBLIC_TITLE} needs to perform a contract with you</li>
|
|
||||||
<li>You have given {process.env.NEXT_PUBLIC_TITLE} permission to do so</li>
|
|
||||||
<li>
|
<li>
|
||||||
Processing your personal information is in {process.env.NEXT_PUBLIC_TITLE} legitimate
|
{process.env.NEXT_PUBLIC_TITLE} needs to perform a contract with you
|
||||||
interests
|
</li>
|
||||||
|
<li>
|
||||||
|
You have given {process.env.NEXT_PUBLIC_TITLE} permission to do so
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Processing your personal information is in{' '}
|
||||||
|
{process.env.NEXT_PUBLIC_TITLE} legitimate interests
|
||||||
</li>
|
</li>
|
||||||
<li>{process.env.NEXT_PUBLIC_TITLE} needs to comply with the law</li>
|
<li>{process.env.NEXT_PUBLIC_TITLE} needs to comply with the law</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{process.env.NEXT_PUBLIC_TITLE} will retain your personal information only for as long as is
|
{process.env.NEXT_PUBLIC_TITLE} will retain your personal information only
|
||||||
necessary for the purposes set out in this Privacy Policy. We will retain and use your
|
for as long as is necessary for the purposes set out in this Privacy
|
||||||
information to the extent necessary to comply with our legal obligations, resolve disputes,
|
Policy. We will retain and use your information to the extent necessary to
|
||||||
and enforce our policies.
|
comply with our legal obligations, resolve disputes, and enforce our
|
||||||
|
policies.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you are a resident of the European Economic Area (EEA), you have certain data protection
|
If you are a resident of the European Economic Area (EEA), you have
|
||||||
rights. If you wish to be informed what Personal Information we hold about you and if you want
|
certain data protection rights. If you wish to be informed what Personal
|
||||||
it to be removed from our systems, please contact us.
|
Information we hold about you and if you want it to be removed from our
|
||||||
|
systems, please contact us.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In certain circumstances, you have the following data protection rights:
|
||||||
</p>
|
</p>
|
||||||
<p>In certain circumstances, you have the following data protection rights:</p>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>The right to access, update or to delete the information we have on you.</li>
|
<li>
|
||||||
|
The right to access, update or to delete the information we have on you.
|
||||||
|
</li>
|
||||||
<li>The right of rectification.</li>
|
<li>The right of rectification.</li>
|
||||||
<li>The right to object.</li>
|
<li>The right to object.</li>
|
||||||
<li>The right of restriction.</li>
|
<li>The right of restriction.</li>
|
||||||
@@ -57,78 +71,88 @@ const PrivacyPolicy = (): JSX.Element => (
|
|||||||
<h2>Log Files</h2>
|
<h2>Log Files</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{process.env.NEXT_PUBLIC_TITLE} follows a standard procedure of using log files. These files
|
{process.env.NEXT_PUBLIC_TITLE} follows a standard procedure of using log
|
||||||
log visitors when they visit websites. All hosting companies do this and a part of hosting
|
files. These files log visitors when they visit websites. All hosting
|
||||||
services' analytics. The information collected by log files include internet protocol (IP)
|
companies do this and a part of hosting services' analytics. The
|
||||||
addresses, browser type, Internet Service Provider (ISP), date and time stamp, referring/exit
|
information collected by log files include internet protocol (IP)
|
||||||
pages, and possibly the number of clicks. These are not linked to any information that is
|
addresses, browser type, Internet Service Provider (ISP), date and time
|
||||||
personally identifiable. The purpose of the information is for analyzing trends, administering
|
stamp, referring/exit pages, and possibly the number of clicks. These are
|
||||||
the site, tracking users' movement on the website, and gathering demographic information.
|
not linked to any information that is personally identifiable. The purpose
|
||||||
|
of the information is for analyzing trends, administering the site,
|
||||||
|
tracking users' movement on the website, and gathering demographic
|
||||||
|
information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Privacy Policies</h2>
|
<h2>Privacy Policies</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You may consult this list to find the Privacy Policy for each of the advertising partners of{' '}
|
You may consult this list to find the Privacy Policy for each of the
|
||||||
{process.env.NEXT_PUBLIC_TITLE}.
|
advertising partners of {process.env.NEXT_PUBLIC_TITLE}.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web
|
Third-party ad servers or ad networks uses technologies like cookies,
|
||||||
Beacons that are used in their respective advertisements and links that appear on{' '}
|
JavaScript, or Web Beacons that are used in their respective
|
||||||
{process.env.NEXT_PUBLIC_TITLE}, which are sent directly to users' browser. They automatically
|
advertisements and links that appear on {process.env.NEXT_PUBLIC_TITLE},
|
||||||
receive your IP address when this occurs. These technologies are used to measure the
|
which are sent directly to users' browser. They automatically receive your
|
||||||
effectiveness of their advertising campaigns and/or to personalize the advertising content
|
IP address when this occurs. These technologies are used to measure the
|
||||||
that you see on websites that you visit.
|
effectiveness of their advertising campaigns and/or to personalize the
|
||||||
|
advertising content that you see on websites that you visit.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Note that {process.env.NEXT_PUBLIC_TITLE} has no access to or control over these cookies that
|
Note that {process.env.NEXT_PUBLIC_TITLE} has no access to or control over
|
||||||
are used by third-party advertisers.
|
these cookies that are used by third-party advertisers.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Third Party Privacy Policies</h2>
|
<h2>Third Party Privacy Policies</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{process.env.NEXT_PUBLIC_TITLE}'s Privacy Policy does not apply to other advertisers or
|
{process.env.NEXT_PUBLIC_TITLE}'s Privacy Policy does not apply to other
|
||||||
websites. Thus, we are advising you to consult the respective Privacy Policies of these
|
advertisers or websites. Thus, we are advising you to consult the
|
||||||
third-party ad servers for more detailed information. It may include their practices and
|
respective Privacy Policies of these third-party ad servers for more
|
||||||
instructions about how to opt-out of certain options.{' '}
|
detailed information. It may include their practices and instructions
|
||||||
|
about how to opt-out of certain options.{' '}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You can choose to disable cookies through your individual browser options. To know more
|
You can choose to disable cookies through your individual browser options.
|
||||||
detailed information about cookie management with specific web browsers, it can be found at
|
To know more detailed information about cookie management with specific
|
||||||
the browsers' respective websites.
|
web browsers, it can be found at the browsers' respective websites.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Children's Information</h2>
|
<h2>Children's Information</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Another part of our priority is adding protection for children while using the internet. We
|
Another part of our priority is adding protection for children while using
|
||||||
encourage parents and guardians to observe, participate in, and/or monitor and guide their
|
the internet. We encourage parents and guardians to observe, participate
|
||||||
online activity.
|
in, and/or monitor and guide their online activity.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{process.env.NEXT_PUBLIC_TITLE} does not knowingly collect any Personal Identifiable
|
{process.env.NEXT_PUBLIC_TITLE} does not knowingly collect any Personal
|
||||||
Information from children under the age of 13. If you think that your child provided this kind
|
Identifiable Information from children under the age of 13. If you think
|
||||||
of information on our website, we strongly encourage you to contact us immediately and we will
|
that your child provided this kind of information on our website, we
|
||||||
do our best efforts to promptly remove such information from our records.
|
strongly encourage you to contact us immediately and we will do our best
|
||||||
|
efforts to promptly remove such information from our records.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Online Privacy Policy Only</h2>
|
<h2>Online Privacy Policy Only</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Our Privacy Policy applies only to our online activities and is valid for visitors to our
|
Our Privacy Policy applies only to our online activities and is valid for
|
||||||
website with regards to the information that they shared and/or collect in{' '}
|
visitors to our website with regards to the information that they shared
|
||||||
{process.env.NEXT_PUBLIC_TITLE}. This policy is not applicable to any information collected
|
and/or collect in {process.env.NEXT_PUBLIC_TITLE}. This policy is not
|
||||||
offline or via channels other than this website.
|
applicable to any information collected offline or via channels other than
|
||||||
|
this website.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Consent</h2>
|
<h2>Consent</h2>
|
||||||
|
|
||||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms.</p>
|
<p>
|
||||||
|
By using our website, you hereby consent to our Privacy Policy and agree
|
||||||
|
to its terms.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const Container = ({ children }: ContainerProps): JSX.Element => {
|
|||||||
const { user, signOut } = useAuth();
|
const { user, signOut } = useAuth();
|
||||||
if (user)
|
if (user)
|
||||||
return (
|
return (
|
||||||
<div className="w-80 md:w-96 order-first lg:order-last">
|
<div className="order-first w-80 md:w-96 lg:order-last">
|
||||||
<p>Hello {user.email}! 👋 You are already logged in</p>
|
<p>Hello {user.email}! 👋 You are already logged in</p>
|
||||||
<button className="btn btn-primary" onClick={() => signOut()}>
|
<button className="btn btn-primary" onClick={() => signOut()}>
|
||||||
Sign out
|
Sign out
|
||||||
|
|||||||
@@ -1,199 +1,233 @@
|
|||||||
const Terms = (): JSX.Element => (
|
const Terms = (): JSX.Element => (
|
||||||
<div className="max-w-xl text-left m-auto py-10">
|
<div className="max-w-xl py-10 m-auto text-left">
|
||||||
<h1>Terms and Conditions</h1>
|
<h1>Terms and Conditions</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The following terms and conditions (collectively, these "Terms and Conditions") apply to your
|
The following terms and conditions (collectively, these "Terms and
|
||||||
use of <span className="website_url">https://www.supanextail.dev</span>, including any
|
Conditions") apply to your use of{' '}
|
||||||
content, functionality and services offered on or via{' '}
|
<span className="website_url">https://www.supanextail.dev</span>,
|
||||||
<span className="website_url">https://www.supanextail.dev</span> (the "Website").
|
including any content, functionality and services offered on or via{' '}
|
||||||
|
<span className="website_url">https://www.supanextail.dev</span> (the
|
||||||
|
"Website").
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Please read the Terms and Conditions carefully before you start using{' '}
|
Please read the Terms and Conditions carefully before you start using{' '}
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>, because by using the
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>,
|
||||||
Website you accept and agree to be bound and abide by these Terms and Conditions.
|
because by using the Website you accept and agree to be bound and abide by
|
||||||
|
these Terms and Conditions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
These Terms and Conditions are effective as of <span className="date">06/22/2021</span>. We
|
These Terms and Conditions are effective as of{' '}
|
||||||
expressly reserve the right to change these Terms and Conditions from time to time without
|
<span className="date">06/22/2021</span>. We expressly reserve the right
|
||||||
notice to you. You acknowledge and agree that it is your responsibility to review this Website
|
to change these Terms and Conditions from time to time without notice to
|
||||||
and these Terms and Conditions from time to time and to familiarize yourself with any
|
you. You acknowledge and agree that it is your responsibility to review
|
||||||
modifications. Your continued use of this Website after such modifications will constitute
|
this Website and these Terms and Conditions from time to time and to
|
||||||
acknowledgement of the modified Terms and Conditions and agreement to abide and be bound by
|
familiarize yourself with any modifications. Your continued use of this
|
||||||
the modified Terms and Conditions.
|
Website after such modifications will constitute acknowledgement of the
|
||||||
|
modified Terms and Conditions and agreement to abide and be bound by the
|
||||||
|
modified Terms and Conditions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Conduct on Website</h2>
|
<h2>Conduct on Website</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Your use of the Website is subject to all applicable laws and regulations, and you are solely
|
Your use of the Website is subject to all applicable laws and regulations,
|
||||||
responsible for the substance of your communications through the Website.
|
and you are solely responsible for the substance of your communications
|
||||||
|
through the Website.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
By posting information in or otherwise using any communications service, chat room, message
|
By posting information in or otherwise using any communications service,
|
||||||
board, newsgroup, software library, or other interactive service that may be available to you
|
chat room, message board, newsgroup, software library, or other
|
||||||
on or through this Website, you agree that you will not upload, share, post, or otherwise
|
interactive service that may be available to you on or through this
|
||||||
distribute or facilitate distribution of any content — including text, communications,
|
Website, you agree that you will not upload, share, post, or otherwise
|
||||||
software, images, sounds, data, or other information — that:
|
distribute or facilitate distribution of any content — including text,
|
||||||
|
communications, software, images, sounds, data, or other information —
|
||||||
|
that:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Is unlawful, threatening, abusive, harassing, defamatory, libelous, deceptive, fraudulent,
|
Is unlawful, threatening, abusive, harassing, defamatory, libelous,
|
||||||
invasive of another's privacy, tortious, contains explicit or graphic descriptions or
|
deceptive, fraudulent, invasive of another's privacy, tortious, contains
|
||||||
accounts of sexual acts (including but not limited to sexual language of a violent or
|
explicit or graphic descriptions or accounts of sexual acts (including
|
||||||
threatening nature directed at another individual or group of individuals), or otherwise
|
but not limited to sexual language of a violent or threatening nature
|
||||||
|
directed at another individual or group of individuals), or otherwise
|
||||||
violates our rules or policies
|
violates our rules or policies
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Victimizes, harasses, degrades, or intimidates an individual or group of individuals on the
|
Victimizes, harasses, degrades, or intimidates an individual or group of
|
||||||
basis of religion, gender, sexual orientation, race, ethnicity, age, or disability
|
individuals on the basis of religion, gender, sexual orientation, race,
|
||||||
|
ethnicity, age, or disability
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Infringes on any patent, trademark, trade secret, copyright, right of publicity, or other
|
Infringes on any patent, trademark, trade secret, copyright, right of
|
||||||
proprietary right of any party
|
publicity, or other proprietary right of any party
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Constitutes unauthorized or unsolicited advertising, junk or bulk email (also known as
|
Constitutes unauthorized or unsolicited advertising, junk or bulk email
|
||||||
"spamming"), chain letters, any other form of unauthorized solicitation, or any form of
|
(also known as "spamming"), chain letters, any other form of
|
||||||
lottery or gambling
|
unauthorized solicitation, or any form of lottery or gambling
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Contains software viruses or any other computer code, files, or programs that are designed
|
Contains software viruses or any other computer code, files, or programs
|
||||||
or intended to disrupt, damage, or limit the functioning of any software, hardware, or
|
that are designed or intended to disrupt, damage, or limit the
|
||||||
telecommunications equipment or to damage or obtain unauthorized access to any data or other
|
functioning of any software, hardware, or telecommunications equipment
|
||||||
|
or to damage or obtain unauthorized access to any data or other
|
||||||
information of any third party
|
information of any third party
|
||||||
</li>
|
</li>
|
||||||
<li>Impersonates any person or entity, including any of our employees or representatives</li>
|
<li>
|
||||||
|
Impersonates any person or entity, including any of our employees or
|
||||||
|
representatives
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
We neither endorse nor assume any liability for the contents of any material uploaded or
|
We neither endorse nor assume any liability for the contents of any
|
||||||
submitted by third party users of the Website. We generally do not pre-screen, monitor, or
|
material uploaded or submitted by third party users of the Website. We
|
||||||
edit the content posted by users of communications services, chat rooms, message boards,
|
generally do not pre-screen, monitor, or edit the content posted by users
|
||||||
newsgroups, software libraries, or other interactive services that may be available on or
|
of communications services, chat rooms, message boards, newsgroups,
|
||||||
through this Website.
|
software libraries, or other interactive services that may be available on
|
||||||
|
or through this Website.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
However, we and our agents have the right at their sole discretion to remove any content that,
|
However, we and our agents have the right at their sole discretion to
|
||||||
in our judgment, does not comply with these Terms of Use and any other rules of user conduct
|
remove any content that, in our judgment, does not comply with these Terms
|
||||||
for our Website, or is otherwise harmful, objectionable, or inaccurate. We are not responsible
|
of Use and any other rules of user conduct for our Website, or is
|
||||||
for any failure or delay in removing such content. You hereby consent to such removal and
|
otherwise harmful, objectionable, or inaccurate. We are not responsible
|
||||||
waive any claim against us arising out of such removal of content.
|
for any failure or delay in removing such content. You hereby consent to
|
||||||
|
such removal and waive any claim against us arising out of such removal of
|
||||||
|
content.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You agree that we may at any time, and at our sole discretion, terminate your membership,
|
You agree that we may at any time, and at our sole discretion, terminate
|
||||||
account, or other affiliation with our site without prior notice to you for violating any of
|
your membership, account, or other affiliation with our site without prior
|
||||||
the above provisions.
|
notice to you for violating any of the above provisions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
In addition, you acknowledge that we will cooperate fully with investigations of violations of
|
In addition, you acknowledge that we will cooperate fully with
|
||||||
systems or network security at other sites, including cooperating with law enforcement
|
investigations of violations of systems or network security at other
|
||||||
authorities in investigating suspected criminal violations.
|
sites, including cooperating with law enforcement authorities in
|
||||||
|
investigating suspected criminal violations.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Intellectual Property</h2>
|
<h2>Intellectual Property</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
By accepting these Terms and Conditions, you acknowledge and agree that all content presented
|
By accepting these Terms and Conditions, you acknowledge and agree that
|
||||||
to you on this Website is protected by copyrights, trademarks, service marks, patents or other
|
all content presented to you on this Website is protected by copyrights,
|
||||||
proprietary rights and laws, and is the sole property of{' '}
|
trademarks, service marks, patents or other proprietary rights and laws,
|
||||||
|
and is the sole property of{' '}
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>.
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You are only permitted to use the content as expressly authorized by us or the specific
|
You are only permitted to use the content as expressly authorized by us or
|
||||||
content provider. Except for a single copy made for personal use only, you may not copy,
|
the specific content provider. Except for a single copy made for personal
|
||||||
reproduce, modify, republish, upload, post, transmit, or distribute any documents or
|
use only, you may not copy, reproduce, modify, republish, upload, post,
|
||||||
information from this Website in any form or by any means without prior written permission
|
transmit, or distribute any documents or information from this Website in
|
||||||
from us or the specific content provider, and you are solely responsible for obtaining
|
any form or by any means without prior written permission from us or the
|
||||||
permission before reusing any copyrighted material that is available on this Website.
|
specific content provider, and you are solely responsible for obtaining
|
||||||
|
permission before reusing any copyrighted material that is available on
|
||||||
|
this Website.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Third Party Websites</h2>
|
<h2>Third Party Websites</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This Website may link you to other sites on the Internet or otherwise include references to
|
This Website may link you to other sites on the Internet or otherwise
|
||||||
information, documents, software, materials and/or services provided by other parties. These
|
include references to information, documents, software, materials and/or
|
||||||
websites may contain information or material that some people may find inappropriate or
|
services provided by other parties. These websites may contain information
|
||||||
offensive.
|
or material that some people may find inappropriate or offensive.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
These other websites and parties are not under our control, and you acknowledge that we are
|
These other websites and parties are not under our control, and you
|
||||||
not responsible for the accuracy, copyright compliance, legality, decency, or any other aspect
|
acknowledge that we are not responsible for the accuracy, copyright
|
||||||
of the content of such sites, nor are we responsible for errors or omissions in any references
|
compliance, legality, decency, or any other aspect of the content of such
|
||||||
to other parties or their products and services. The inclusion of such a link or reference is
|
sites, nor are we responsible for errors or omissions in any references to
|
||||||
provided merely as a convenience and does not imply endorsement of, or association with, the
|
other parties or their products and services. The inclusion of such a link
|
||||||
Website or party by us, or any warranty of any kind, either express or implied.
|
or reference is provided merely as a convenience and does not imply
|
||||||
|
endorsement of, or association with, the Website or party by us, or any
|
||||||
|
warranty of any kind, either express or implied.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Disclaimer of Warranties, Limitations of Liability and Indemnification</h2>
|
<h2>
|
||||||
|
Disclaimer of Warranties, Limitations of Liability and Indemnification
|
||||||
|
</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Your use of <span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> is at your
|
Your use of{' '}
|
||||||
sole risk. The Website is provided "as is" and "as available". We disclaim all warranties of
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> is
|
||||||
any kind, express or implied, including, without limitation, the warranties of
|
at your sole risk. The Website is provided "as is" and "as available". We
|
||||||
merchantability, fitness for a particular purpose and non-infringement.
|
disclaim all warranties of any kind, express or implied, including,
|
||||||
|
without limitation, the warranties of merchantability, fitness for a
|
||||||
|
particular purpose and non-infringement.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
We are not liable for damages, direct or consequential, resulting from your use of the
|
We are not liable for damages, direct or consequential, resulting from
|
||||||
Website, and you agree to defend, indemnify and hold us harmless from any claims, losses,
|
your use of the Website, and you agree to defend, indemnify and hold us
|
||||||
liability costs and expenses (including but not limites to attorney's fees) arising from your
|
harmless from any claims, losses, liability costs and expenses (including
|
||||||
violation of any third-party's rights. You acknowledge that you have only a limited,
|
but not limites to attorney's fees) arising from your violation of any
|
||||||
non-exclusive, nontransferable license to use the Website. Because the Website is not error or
|
third-party's rights. You acknowledge that you have only a limited,
|
||||||
bug free, you agree that you will use it carefully and avoid using it ways which might result
|
non-exclusive, nontransferable license to use the Website. Because the
|
||||||
in any loss of your or any third party's property or information.
|
Website is not error or bug free, you agree that you will use it carefully
|
||||||
|
and avoid using it ways which might result in any loss of your or any
|
||||||
|
third party's property or information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Term and termination</h2>
|
<h2>Term and termination</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This Terms and Conditions will become effective in relation to you when you create a{' '}
|
This Terms and Conditions will become effective in relation to you when
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> account or when you
|
you create a{' '}
|
||||||
start using the <span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> and will
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||||
remain effective until terminated by you or by us.{' '}
|
account or when you start using the{' '}
|
||||||
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> and
|
||||||
|
will remain effective until terminated by you or by us.{' '}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> reserves the right to
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||||
terminate this Terms and Conditions or suspend your account at any time in case of
|
reserves the right to terminate this Terms and Conditions or suspend your
|
||||||
unauthorized, or suspected unauthorized use of the Website whether in contravention of this
|
account at any time in case of unauthorized, or suspected unauthorized use
|
||||||
Terms and Conditions or otherwise. If{' '}
|
of the Website whether in contravention of this Terms and Conditions or
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> terminates this Terms
|
otherwise. If{' '}
|
||||||
and Conditions, or suspends your account for any of the reasons set out in this section,{' '}
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> shall have no liability
|
terminates this Terms and Conditions, or suspends your account for any of
|
||||||
or responsibility to you.
|
the reasons set out in this section,{' '}
|
||||||
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||||
|
shall have no liability or responsibility to you.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Assignment</h2>
|
<h2>Assignment</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> may assign this Terms
|
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> may
|
||||||
and Conditions or any part of it without restrictions. You may not assign this Terms and
|
assign this Terms and Conditions or any part of it without restrictions.
|
||||||
Conditions or any part of it to any third party.
|
You may not assign this Terms and Conditions or any part of it to any
|
||||||
|
third party.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Governing Law</h2>
|
<h2>Governing Law</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
These Terms and Conditions and any dispute or claim arising out of, or related to them, shall
|
These Terms and Conditions and any dispute or claim arising out of, or
|
||||||
be governed by and construed in accordance with the internal laws of the{' '}
|
related to them, shall be governed by and construed in accordance with the
|
||||||
<span className="country">fr</span> without giving effect to any choice or conflict of law
|
internal laws of the <span className="country">fr</span> without giving
|
||||||
provision or rule.
|
effect to any choice or conflict of law provision or rule.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Any legal suit, action or proceeding arising out of, or related to, these Terms of Service or
|
Any legal suit, action or proceeding arising out of, or related to, these
|
||||||
the Website shall be instituted exclusively in the federal courts of{' '}
|
Terms of Service or the Website shall be instituted exclusively in the
|
||||||
<span className="country">fr</span>.
|
federal courts of <span className="country">fr</span>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ type CardLandingProps = {
|
|||||||
|
|
||||||
const CardLanding = ({ image, title, text }: CardLandingProps): JSX.Element => {
|
const CardLanding = ({ image, title, text }: CardLandingProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="w-80 h-48 p-5 sm:ml-5 mb-5 bg-base-100 flex">
|
<div className="flex h-48 p-5 mb-5 w-80 sm:ml-5 bg-base-100">
|
||||||
<div>
|
<div>
|
||||||
<div className="rounded-full w-12 h-12 border flex bg-neutral-content">
|
<div className="flex w-12 h-12 border rounded-full bg-neutral-content">
|
||||||
<div className="m-auto flex">
|
<div className="flex m-auto">
|
||||||
<Image src={image} width={24} height={24} alt={`${title} logo`} />
|
<Image src={image} width={24} height={24} alt={`${title} logo`} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-8">
|
<div className="ml-8">
|
||||||
<p className="font-semibold font-title text-lg">{title}</p>
|
<p className="text-lg font-semibold font-title">{title}</p>
|
||||||
<p className="mt-3">{text}</p>
|
<p className="mt-3">{text}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ type KeyFeatureProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const KeyFeature = ({ children }: KeyFeatureProps): JSX.Element => (
|
const KeyFeature = ({ children }: KeyFeatureProps): JSX.Element => (
|
||||||
<div className="shadow-sm p-5 mb-5 bg-base-100 flex italic">
|
<div className="flex p-5 mb-5 italic shadow-sm bg-base-100">
|
||||||
<div className="p-2 bg-accent-focus w-12 h-12 text-white rounded-sm my-auto flex">
|
<div className="flex w-12 h-12 p-2 my-auto text-white rounded-sm bg-accent-focus">
|
||||||
<FiStar className="text-2xl m-auto" />
|
<FiStar className="m-auto text-2xl" />
|
||||||
</div>
|
</div>
|
||||||
<div className="m-auto ml-3">{children}</div>
|
<div className="m-auto ml-3">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
type LoginProps = {
|
type LoginProps = {
|
||||||
resetPassword: (email: string) => Promise<{ error: { message: string } }>;
|
resetPassword: (email: string) => Promise<{ error: { message: string } }>;
|
||||||
signIn: ({}) => Promise<{ data: Record<string, unknown>; error: { message: string } }>;
|
signIn: ({}) => Promise<{
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
error: { message: string };
|
||||||
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
||||||
@@ -28,21 +31,28 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
signIn({
|
signIn({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
}).then((result: { data: Record<string, unknown>; error: { message: string } }) => {
|
}).then(
|
||||||
|
(result: {
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
error: { message: string };
|
||||||
|
}) => {
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
toast.error(result.error.message);
|
toast.error(result.error.message);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-10 bg-base-100 md:flex-1 rounded-md text-base-content shadow-md max-w-sm font-body">
|
<div className="max-w-sm p-10 rounded-md shadow-md md:flex-1 bg-base-100 text-base-content font-body">
|
||||||
{!forgot && (
|
{!forgot && (
|
||||||
<>
|
<>
|
||||||
<h3 className="my-4 text-2xl font-semibold font-title">Account Login</h3>
|
<h3 className="my-4 text-2xl font-semibold font-title">
|
||||||
|
Account Login
|
||||||
|
</h3>
|
||||||
<form action="#" className="flex flex-col space-y-5">
|
<form action="#" className="flex flex-col space-y-5">
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
<label htmlFor="email" className="text-sm">
|
<label htmlFor="email" className="text-sm">
|
||||||
@@ -68,8 +78,7 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setForgot(true);
|
setForgot(true);
|
||||||
}}
|
}}
|
||||||
className="text-sm text-blue-600 hover:underline focus:text-blue-800"
|
className="text-sm text-blue-600 hover:underline focus:text-blue-800">
|
||||||
>
|
|
||||||
Forgot Password?
|
Forgot Password?
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,11 +95,10 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary w-full"
|
className="w-full btn btn-primary"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
login(event);
|
login(event);
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
Log in
|
Log in
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,16 +110,17 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex flex-col space-y-4">
|
<div className="flex flex-col space-y-4">
|
||||||
<button
|
<button
|
||||||
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border border-base-200 rounded-md group hover:bg-base-300 focus:outline-none "
|
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
signIn({ provider: 'google' });
|
signIn({ provider: 'google' });
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
<div className="text-base-content">
|
<div className="text-base-content">
|
||||||
<IoLogoGoogle />
|
<IoLogoGoogle />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-medium text-base-content">Gmail</span>
|
<span className="text-sm font-medium text-base-content">
|
||||||
|
Gmail
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,7 +132,9 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
<h3 className="my-4 text-2xl font-semibold">Password recovery</h3>
|
<h3 className="my-4 text-2xl font-semibold">Password recovery</h3>
|
||||||
<form action="#" className="flex flex-col space-y-5">
|
<form action="#" className="flex flex-col space-y-5">
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
<label htmlFor="email" className="text-sm font-semibold text-gray-500">
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="text-sm font-semibold text-gray-500">
|
||||||
Email address
|
Email address
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -140,12 +151,11 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary w-full btn-sm"
|
className="w-full btn btn-primary btn-sm"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
resetPasswordLogin();
|
resetPasswordLogin();
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
Recover my password
|
Recover my password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,8 +164,7 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setForgot(false);
|
setForgot(false);
|
||||||
}}
|
}}
|
||||||
className="text-sm text-blue-600 hover:underline focus:text-blue-800"
|
className="text-sm text-blue-600 hover:underline focus:text-blue-800">
|
||||||
>
|
|
||||||
Go back to sign in
|
Go back to sign in
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,8 +4,14 @@ import { toast } from 'react-toastify';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
type SignUpPanelProps = {
|
type SignUpPanelProps = {
|
||||||
signIn: ({}) => Promise<{ data: Record<string, unknown>; error: { message: string } }>;
|
signIn: ({}) => Promise<{
|
||||||
signUp: ({}) => Promise<{ data: Record<string, unknown>; error: { message: string } }>;
|
data: Record<string, unknown>;
|
||||||
|
error: { message: string };
|
||||||
|
}>;
|
||||||
|
signUp: ({}) => Promise<{
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
error: { message: string };
|
||||||
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
||||||
@@ -25,7 +31,9 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
toast.error(result.error.message);
|
toast.error(result.error.message);
|
||||||
} else if (result.data?.confirmation_sent_at) {
|
} else if (result.data?.confirmation_sent_at) {
|
||||||
console.log(result.data.confirmation_sent_at);
|
console.log(result.data.confirmation_sent_at);
|
||||||
toast.success('A confirmation email has been sent to you, watch your mailbox!');
|
toast.success(
|
||||||
|
'A confirmation email has been sent to you, watch your mailbox!'
|
||||||
|
);
|
||||||
} else if (result.data) {
|
} else if (result.data) {
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
@@ -33,8 +41,10 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-10 bg-base-100 md:flex-1 rounded-md text-base-content shadow-md max-w-sm font-body">
|
<div className="max-w-sm p-10 rounded-md shadow-md md:flex-1 bg-base-100 text-base-content font-body">
|
||||||
<h3 className="my-4 text-2xl font-semibold font-title">Account Sign Up</h3>
|
<h3 className="my-4 text-2xl font-semibold font-title">
|
||||||
|
Account Sign Up
|
||||||
|
</h3>
|
||||||
<form action="#" className="flex flex-col space-y-5">
|
<form action="#" className="flex flex-col space-y-5">
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
<label htmlFor="email" className="text-sm">
|
<label htmlFor="email" className="text-sm">
|
||||||
@@ -66,11 +76,10 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
id="loginBtn"
|
id="loginBtn"
|
||||||
className="btn btn-primary w-full"
|
className="w-full btn btn-primary"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
signup(event);
|
signup(event);
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
Sign Up
|
Sign Up
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,16 +91,17 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex flex-col space-y-4">
|
<div className="flex flex-col space-y-4">
|
||||||
<button
|
<button
|
||||||
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border border-base-200 rounded-md group hover:bg-base-300 focus:outline-none "
|
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
signIn({ provider: 'google' });
|
signIn({ provider: 'google' });
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
<div className="text-base-content">
|
<div className="text-base-content">
|
||||||
<IoLogoGoogle />
|
<IoLogoGoogle />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-medium text-base-content">Gmail</span>
|
<span className="text-sm font-medium text-base-content">
|
||||||
|
Gmail
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ const theme = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ThemeToggle = (): JSX.Element => {
|
const ThemeToggle = (): JSX.Element => {
|
||||||
const [activeTheme, setActiveTheme] = useState(document.body.dataset.theme || '');
|
const [activeTheme, setActiveTheme] = useState(
|
||||||
|
document.body.dataset.theme || ''
|
||||||
|
);
|
||||||
const inactiveTheme = activeTheme === 'supaTheme' ? 'dark' : 'supaTheme';
|
const inactiveTheme = activeTheme === 'supaTheme' ? 'dark' : 'supaTheme';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import Document, {
|
|||||||
} from 'next/document';
|
} from 'next/document';
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
|
static async getInitialProps(
|
||||||
|
ctx: DocumentContext
|
||||||
|
): Promise<DocumentInitialProps> {
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
|
|
||||||
return initialProps;
|
return initialProps;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import { supabase } from 'utils/supabaseClient';
|
import { supabase } from 'utils/supabaseClient';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> {
|
||||||
supabase.auth.api.setAuthCookie(req, res);
|
supabase.auth.api.setAuthCookie(req, res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { supabase } from 'utils/supabaseClient';
|
import { supabase } from 'utils/supabaseClient';
|
||||||
|
|
||||||
// Example of how to verify and get user data server-side.
|
// Example of how to verify and get user data server-side.
|
||||||
const getUser = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
|
const getUser = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> => {
|
||||||
const token = req.headers.token;
|
const token = req.headers.token;
|
||||||
|
|
||||||
if (typeof token !== 'string') {
|
if (typeof token !== 'string') {
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ const limiter = initMiddleware(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
await limiter(req, res);
|
await limiter(req, res);
|
||||||
if (req.method === 'PUT') {
|
if (req.method === 'PUT') {
|
||||||
@@ -45,7 +48,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
.then((result) => {
|
.then((result) => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
message: 'Your email has been succesfully added to the mailing list. Welcome 👋',
|
message:
|
||||||
|
'Your email has been succesfully added to the mailing list. Welcome 👋',
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@@ -2,11 +2,15 @@
|
|||||||
This is a simple contact form for SupaNexTail
|
This is a simple contact form for SupaNexTail
|
||||||
Using Sendgrid.
|
Using Sendgrid.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import sgMail from '@sendgrid/mail';
|
import sgMail from '@sendgrid/mail';
|
||||||
|
|
||||||
const sendGrid = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
|
const sendGrid = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> => {
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
sgMail.setApiKey(process.env.SENDGRID_SECRET || '');
|
sgMail.setApiKey(process.env.SENDGRID_SECRET || '');
|
||||||
|
|
||||||
@@ -21,7 +25,9 @@ const sendGrid = async (req: NextApiRequest, res: NextApiResponse): Promise<void
|
|||||||
sgMail
|
sgMail
|
||||||
.send(msg)
|
.send(msg)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
res.status(200).send({ message: 'Your email has been sent', success: true });
|
res
|
||||||
|
.status(200)
|
||||||
|
.send({ message: 'Your email has been sent', success: true });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
apiVersion: '2020-08-27',
|
apiVersion: '2020-08-27',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
await limiter(req, res);
|
await limiter(req, res);
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* Dont forget to create your customer portal on Stripe
|
/* Dont forget to create your customer portal on Stripe
|
||||||
https://dashboard.stripe.com/test/settings/billing/portal */
|
https://dashboard.stripe.com/test/settings/billing/portal */
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import Cors from 'cors';
|
import Cors from 'cors';
|
||||||
@@ -25,7 +26,10 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
apiVersion: '2020-08-27',
|
apiVersion: '2020-08-27',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
await limiter(req, res);
|
await limiter(req, res);
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
maxNetworkRetries: 2,
|
maxNetworkRetries: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
await limiter(req, res);
|
await limiter(req, res);
|
||||||
|
|
||||||
@@ -63,11 +66,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const sig = req.headers['stripe-signature'] as string;
|
const sig = req.headers['stripe-signature'] as string;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK || '');
|
event = stripe.webhooks.constructEvent(
|
||||||
|
buf,
|
||||||
|
sig,
|
||||||
|
process.env.STRIPE_WEBHOOK || ''
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
console.log(`⚠️ Webhook signature verification failed.`);
|
console.log(`⚠️ Webhook signature verification failed.`);
|
||||||
console.log(`⚠️ Check the env file and enter the correct webhook secret.`);
|
console.log(
|
||||||
|
`⚠️ Check the env file and enter the correct webhook secret.`
|
||||||
|
);
|
||||||
return res.send(400);
|
return res.send(400);
|
||||||
}
|
}
|
||||||
// Extract the object from the event.
|
// Extract the object from the event.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const DashboardPage = ({
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [router, user]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSession(supabase.auth.session());
|
setSession(supabase.auth.session());
|
||||||
@@ -100,7 +100,9 @@ export async function getServerSideProps(context: NextPageContext) {
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
user,
|
user,
|
||||||
plan: subscription?.items.data[0].price.id ? subscription?.items.data[0].price.id : null,
|
plan: subscription?.items.data[0].price.id
|
||||||
|
? subscription?.items.data[0].price.id
|
||||||
|
: null,
|
||||||
profile,
|
profile,
|
||||||
// Retrieve the name of the subscription plan (Don't forget to add nickname to your prices)
|
// Retrieve the name of the subscription plan (Don't forget to add nickname to your prices)
|
||||||
planName: plan?.paid_user
|
planName: plan?.paid_user
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@import "tailwindcss/tailwind.css";
|
@import 'tailwindcss/tailwind.css';
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Poppins:wght@400;600;700;800&display=swap");
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Poppins:wght@400;600;700;800&display=swap');
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
@@ -10,27 +10,27 @@ You can setup basic rules for your headers/text here
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
h1 {
|
h1 {
|
||||||
@apply text-5xl mb-5 font-bold font-title;
|
@apply mb-5 text-5xl font-bold font-title;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
@apply text-2xl mt-2 mb-3 font-title;
|
@apply mt-2 mb-3 text-2xl font-title;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
@apply text-left mb-5 font-body text-sm text-base-200 leading-loose;
|
@apply mb-5 text-sm leading-loose text-left font-body text-base-200;
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
@apply list-disc ml-10 mb-3 font-body text-sm text-base-200 leading-loose;
|
@apply mb-3 ml-10 text-sm leading-loose list-disc font-body text-base-200;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-btn {
|
.nav-btn {
|
||||||
position: relative;
|
position: relative;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@apply text-base-200 my-auto;
|
@apply my-auto text-base-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-btn::before {
|
.nav-btn::before {
|
||||||
content: "";
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -47,5 +47,5 @@ You can setup basic rules for your headers/text here
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply font-normal
|
@apply font-normal;
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,10 @@ const Home = (): JSX.Element => (
|
|||||||
|
|
||||||
<meta property="og:url" content="https://supanextail.dev/" />
|
<meta property="og:url" content="https://supanextail.dev/" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:title" content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`} />
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}
|
||||||
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:description"
|
property="og:description"
|
||||||
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
||||||
@@ -27,13 +30,22 @@ const Home = (): JSX.Element => (
|
|||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta property="twitter:domain" content="supanextail.dev" />
|
<meta property="twitter:domain" content="supanextail.dev" />
|
||||||
<meta property="twitter:url" content="https://supanextail.dev/ogimage.png" />
|
<meta
|
||||||
<meta name="twitter:title" content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`} />
|
property="twitter:url"
|
||||||
|
content="https://supanextail.dev/ogimage.png"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}
|
||||||
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="twitter:description"
|
name="twitter:description"
|
||||||
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
||||||
/>
|
/>
|
||||||
<meta name="twitter:image" content="https://supanextail.dev/ogimage.png" />
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="https://supanextail.dev/ogimage.png"
|
||||||
|
/>
|
||||||
<meta charSet="UTF-8" />
|
<meta charSet="UTF-8" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const LoginPage = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className="flex flex-wrap justify-evenly w-full mt-20">
|
<div className="flex flex-wrap w-full mt-20 justify-evenly">
|
||||||
<Login signIn={signIn} resetPassword={resetPassword} />
|
<Login signIn={signIn} resetPassword={resetPassword} />
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const SignUpPage = (): JSX.Element => (
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className="flex flex-wrap justify-evenly w-full mt-20">
|
<div className="flex flex-wrap w-full mt-20 justify-evenly">
|
||||||
<AuthText />
|
<AuthText />
|
||||||
<AuthComponent />
|
<AuthComponent />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -19,7 +19,8 @@ export const AuthProvider = ({ children }) => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
// Listen for changes on auth state (logged in, signed out, etc.)
|
// Listen for changes on auth state (logged in, signed out, etc.)
|
||||||
const { data: listener } = supabase.auth.onAuthStateChange(async (event, session) => {
|
const { data: listener } = supabase.auth.onAuthStateChange(
|
||||||
|
async (event, session) => {
|
||||||
if ((event === 'SIGNED_OUT') | (event === 'SIGNED_IN')) {
|
if ((event === 'SIGNED_OUT') | (event === 'SIGNED_IN')) {
|
||||||
fetch('/api/auth', {
|
fetch('/api/auth', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -33,7 +34,8 @@ export const AuthProvider = ({ children }) => {
|
|||||||
setUser(session?.user ?? null);
|
setUser(session?.user ?? null);
|
||||||
setSession(session ?? null);
|
setSession(session ?? null);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
listener?.unsubscribe();
|
listener?.unsubscribe();
|
||||||
@@ -50,7 +52,11 @@ export const AuthProvider = ({ children }) => {
|
|||||||
session,
|
session,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <AuthContext.Provider value={value}>{!loading && children}</AuthContext.Provider>;
|
return (
|
||||||
|
<AuthContext.Provider value={value}>
|
||||||
|
{!loading && children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// export the useAuth hook
|
// export the useAuth hook
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* This is a singleton to ensure we only instantiate Stripe once.
|
* This is a singleton to ensure we only instantiate Stripe once.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Stripe, loadStripe } from '@stripe/stripe-js';
|
import { Stripe, loadStripe } from '@stripe/stripe-js';
|
||||||
|
|
||||||
let stripePromise: Promise<Stripe | null>;
|
let stripePromise: Promise<Stripe | null>;
|
||||||
|
|||||||
Reference in New Issue
Block a user