mirror of
https://github.com/fergalmoran/supanextail.git
synced 2025-12-22 09:17:54 +00:00
First implementation of eslint/prettier
This commit is contained in:
@@ -1,26 +1,23 @@
|
||||
import Image from "next/image";
|
||||
import authImage from "public/auth.png";
|
||||
import Image from 'next/image';
|
||||
import authImage from 'public/auth.png';
|
||||
|
||||
const AuthText = () => {
|
||||
return (
|
||||
<div className='lg:mt-0 max-w-lg flex flex-col text-xl'>
|
||||
<div className='mt-10 mb-3 m-auto'>
|
||||
<Image
|
||||
src={authImage}
|
||||
width={authImage.width / 1.5}
|
||||
height={authImage.height / 1.5}
|
||||
/>
|
||||
</div>
|
||||
<h2 className='text-4xl font-title font-semibold text-center'>
|
||||
Join SupaNexTail for <span className='text-primary'>free</span>!
|
||||
</h2>
|
||||
<p className='mb-5 mt-8 leading-9'>
|
||||
Create your website in a few minutes with our boilerplate. You can use
|
||||
the login system, this will allow you to discover the sample dashboard
|
||||
page.
|
||||
</p>
|
||||
const AuthText = () => (
|
||||
<div className="lg:mt-0 max-w-lg flex flex-col text-xl">
|
||||
<div className="mt-10 mb-3 m-auto">
|
||||
<Image
|
||||
src={authImage}
|
||||
width={authImage.width / 1.5}
|
||||
height={authImage.height / 1.5}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<h2 className="text-4xl font-title font-semibold text-center">
|
||||
Join SupaNexTail for <span className="text-primary">free</span>!
|
||||
</h2>
|
||||
<p className="mb-5 mt-8 leading-9">
|
||||
Create your website in a few minutes with our boilerplate. You can use the
|
||||
login system, this will allow you to discover the sample dashboard page.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default AuthText;
|
||||
|
||||
@@ -4,9 +4,9 @@ the upload.
|
||||
You can tweak the max size, line 47
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { supabase } from "utils/supabaseClient";
|
||||
import { supabase } from 'utils/supabaseClient';
|
||||
|
||||
const Avatar = ({ url, size, onUpload }) => {
|
||||
const [avatarUrl, setAvatarUrl] = useState(null);
|
||||
@@ -19,7 +19,7 @@ const Avatar = ({ url, size, onUpload }) => {
|
||||
async function downloadImage(path) {
|
||||
try {
|
||||
const { data, error } = await supabase.storage
|
||||
.from("avatars")
|
||||
.from('avatars')
|
||||
.download(path);
|
||||
if (error) {
|
||||
throw error;
|
||||
@@ -27,7 +27,7 @@ const Avatar = ({ url, size, onUpload }) => {
|
||||
const url = URL.createObjectURL(data);
|
||||
setAvatarUrl(url);
|
||||
} catch (error) {
|
||||
console.log("Error downloading image: ", error.message);
|
||||
console.log('Error downloading image: ', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,23 @@ const Avatar = ({ url, size, onUpload }) => {
|
||||
setUploading(true);
|
||||
|
||||
if (!event.target.files || event.target.files.length === 0) {
|
||||
throw new Error("You must select an image to upload.");
|
||||
throw new Error('You must select an image to upload.');
|
||||
}
|
||||
|
||||
const file = event.target.files[0];
|
||||
const fileExt = file.name.split(".").pop();
|
||||
const fileExt = file.name.split('.').pop();
|
||||
const fileName = `${Math.random()}.${fileExt}`;
|
||||
const filePath = `${fileName}`;
|
||||
|
||||
if (event.target.files[0].size > 150000) {
|
||||
alert("File is too big!");
|
||||
event.target.value = "";
|
||||
setUploading(false)
|
||||
alert('File is too big!');
|
||||
event.target.value = '';
|
||||
setUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let { error: uploadError } = await supabase.storage
|
||||
.from("avatars")
|
||||
const { error: uploadError } = await supabase.storage
|
||||
.from('avatars')
|
||||
.upload(filePath, file);
|
||||
|
||||
if (uploadError) {
|
||||
@@ -68,30 +68,31 @@ const Avatar = ({ url, size, onUpload }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='m-auto mb-5'>
|
||||
<div className="m-auto mb-5">
|
||||
{avatarUrl ? (
|
||||
<img
|
||||
src={avatarUrl}
|
||||
alt='Avatar'
|
||||
className='avatar rounded-full w-28 h-28 flex m-auto'
|
||||
alt="Avatar"
|
||||
className="avatar rounded-full w-28 h-28 flex m-auto"
|
||||
/>
|
||||
) : (
|
||||
<div className='avatar rounded-full w-28 h-28' />
|
||||
<div className="avatar rounded-full w-28 h-28" />
|
||||
)}
|
||||
<div style={{ width: size }}>
|
||||
<label
|
||||
className='mt-2 btn btn-primary text-center cursor-pointer text-xs btn-sm'
|
||||
htmlFor='single'>
|
||||
{uploading ? "Uploading ..." : "Update my avatar"}
|
||||
className="mt-2 btn btn-primary text-center cursor-pointer text-xs btn-sm"
|
||||
htmlFor="single"
|
||||
>
|
||||
{uploading ? 'Uploading ...' : 'Update my avatar'}
|
||||
</label>
|
||||
<input
|
||||
style={{
|
||||
visibility: "hidden",
|
||||
position: "absolute",
|
||||
visibility: 'hidden',
|
||||
position: 'absolute',
|
||||
}}
|
||||
type='file'
|
||||
id='single'
|
||||
accept='image/*'
|
||||
type="file"
|
||||
id="single"
|
||||
accept="image/*"
|
||||
onChange={uploadAvatar}
|
||||
disabled={uploading}
|
||||
/>
|
||||
|
||||
@@ -1,62 +1,60 @@
|
||||
import CardLanding from "components/UI/CardLanding";
|
||||
import cardAuth from "public/landing/auth.svg";
|
||||
import cardFee from "public/landing/lifetime.svg";
|
||||
import cardPage from "public/landing/page.svg";
|
||||
import cardResponsive from "public/landing/responsive.svg";
|
||||
import cardServer from "public/landing/backend.svg";
|
||||
import cardStripe from "public/landing/stripe.svg";
|
||||
import cardTheme from "public/landing/theme.svg";
|
||||
import CardLanding from 'components/UI/CardLanding';
|
||||
import cardAuth from 'public/landing/auth.svg';
|
||||
import cardFee from 'public/landing/lifetime.svg';
|
||||
import cardPage from 'public/landing/page.svg';
|
||||
import cardResponsive from 'public/landing/responsive.svg';
|
||||
import cardServer from 'public/landing/backend.svg';
|
||||
import cardStripe from 'public/landing/stripe.svg';
|
||||
import cardTheme from 'public/landing/theme.svg';
|
||||
|
||||
const CardsLanding = () => {
|
||||
return (
|
||||
<div className='mt-14'>
|
||||
<h2 className='uppercase font-bold text-4xl tracking-wide text-center'>
|
||||
We've got you covered
|
||||
</h2>
|
||||
<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 so that you only have one goal, to make your SaaS
|
||||
a reality.
|
||||
</p>
|
||||
<div className='flex flex-wrap justify-center mt-10'>
|
||||
<CardLanding
|
||||
image={cardPage}
|
||||
text='7 pages fully designed and easily customizable'
|
||||
title='Templates'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardServer}
|
||||
text='Integrated backend already setup with Next.js API Routes'
|
||||
title='Backend'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardAuth}
|
||||
text='Auth and user management with Supabase'
|
||||
title='Auth'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardResponsive}
|
||||
text='Mobile ready, fully responsive and customizable with Tailwind CSS'
|
||||
title='Responsive'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardTheme}
|
||||
text='Custom themes available and easily switch to dark mode'
|
||||
title='Themes'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardStripe}
|
||||
text='Stripe integration. Fully functional subscription system'
|
||||
title='Payment'
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardFee}
|
||||
text='One-time fee. No subscription, you’ll have access to all the updates'
|
||||
title='Lifetime access'
|
||||
/>
|
||||
</div>
|
||||
const CardsLanding = () => (
|
||||
<div className="mt-14">
|
||||
<h2 className="uppercase font-bold text-4xl tracking-wide text-center">
|
||||
We've got you covered
|
||||
</h2>
|
||||
<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 so that you only have one goal, to make your SaaS a
|
||||
reality.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center mt-10">
|
||||
<CardLanding
|
||||
image={cardPage}
|
||||
text="7 pages fully designed and easily customizable"
|
||||
title="Templates"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardServer}
|
||||
text="Integrated backend already setup with Next.js API Routes"
|
||||
title="Backend"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardAuth}
|
||||
text="Auth and user management with Supabase"
|
||||
title="Auth"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardResponsive}
|
||||
text="Mobile ready, fully responsive and customizable with Tailwind CSS"
|
||||
title="Responsive"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardTheme}
|
||||
text="Custom themes available and easily switch to dark mode"
|
||||
title="Themes"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardStripe}
|
||||
text="Stripe integration. Fully functional subscription system"
|
||||
title="Payment"
|
||||
/>
|
||||
<CardLanding
|
||||
image={cardFee}
|
||||
text="One-time fee. No subscription, you’ll have access to all the updates"
|
||||
title="Lifetime access"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CardsLanding;
|
||||
|
||||
@@ -6,32 +6,32 @@ If you want to change the email provider, don't hesitate to create a new api rou
|
||||
the axios.post here, line 18.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import axios from 'axios';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const Contact = () => {
|
||||
const sendEmail = () => {
|
||||
const name = document.getElementById("name").value;
|
||||
const email = document.getElementById("email").value;
|
||||
const message = document.getElementById("message").value;
|
||||
const name = document.getElementById('name').value;
|
||||
const email = document.getElementById('email').value;
|
||||
const message = document.getElementById('message').value;
|
||||
|
||||
if (name && email && message) {
|
||||
axios
|
||||
.post("/api/sendgrid", { email, name, message })
|
||||
.post('/api/sendgrid', { email, name, message })
|
||||
.then((result) => {
|
||||
if (result.data.success === true) {
|
||||
toast.success(result.data.message);
|
||||
document.getElementById("name").value = "";
|
||||
document.getElementById("email").value = "";
|
||||
document.getElementById("message").value = "";
|
||||
document.getElementById('name').value = '';
|
||||
document.getElementById('email').value = '';
|
||||
document.getElementById('message').value = '';
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
} else {
|
||||
toast.info("Please enter at least one URL", {
|
||||
position: "top-center",
|
||||
toast.info('Please enter at least one URL', {
|
||||
position: 'top-center',
|
||||
autoClose: 2000,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
@@ -42,51 +42,56 @@ const Contact = () => {
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className='max-w-xl m-auto px-5 py-10'>
|
||||
<div className="max-w-xl m-auto px-5 py-10">
|
||||
<div>
|
||||
<div className='flex justify-center'>
|
||||
<h2 className='text-3xl sm:text-4xl text-center mb-5 mt-0 font-bold font-title'>
|
||||
<div className="flex justify-center">
|
||||
<h2 className="text-3xl sm:text-4xl text-center mb-5 mt-0 font-bold font-title">
|
||||
Contact
|
||||
</h2>
|
||||
</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 to report? Don't hesitate!
|
||||
</p>
|
||||
</div>
|
||||
<form className='m-auto mt-5 grid grid-cols-1 gap-4 md:grid-cols-2 p-5'>
|
||||
<div className='flex flex-col max-w-xs'>
|
||||
<label className='font-light mb-4 text-left'>Your Name</label>
|
||||
<form className="m-auto mt-5 grid grid-cols-1 gap-4 md:grid-cols-2 p-5">
|
||||
<div className="flex flex-col max-w-xs">
|
||||
<p className="font-light mb-4 text-left">Your Name</p>
|
||||
<input
|
||||
id='name'
|
||||
name='name'
|
||||
placeholder='Enter your name'
|
||||
className='input input-primary input-bordered'></input>
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Enter your name"
|
||||
className="input input-primary input-bordered"
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col max-w-xs mb-3'>
|
||||
<label className='font-light mb-4 text-left'>Your email</label>
|
||||
<div className="flex flex-col max-w-xs mb-3">
|
||||
<p className="font-light mb-4 text-left">Your email</p>
|
||||
<input
|
||||
id='email'
|
||||
name='email'
|
||||
placeholder='Enter your email adress'
|
||||
className='input input-primary input-bordered'></input>
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Enter your email adress"
|
||||
className="input input-primary input-bordered"
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col col-span-full w-fulll'>
|
||||
<label className='font-light mb-4 text-left'>Message</label>
|
||||
<div className="flex flex-col col-span-full w-fulll">
|
||||
<p className="font-light mb-4 text-left">Message</p>
|
||||
<textarea
|
||||
id='message'
|
||||
name='message'
|
||||
placeholder='Enter your message here...'
|
||||
rows='5'
|
||||
className='input input-primary input-bordered resize-none w-full h-32 pt-2'></textarea>
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Enter your message here..."
|
||||
rows="5"
|
||||
className="input input-primary input-bordered resize-none w-full h-32 pt-2"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className='btn btn-primary btn-sm'
|
||||
type="button"
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
sendEmail();
|
||||
}}>
|
||||
Submit{" "}
|
||||
}}
|
||||
>
|
||||
Submit{' '}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -5,16 +5,16 @@ function with your new elements.
|
||||
It also show you the current subscription plan
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Avatar from "./Avatar";
|
||||
import Image from "next/image";
|
||||
import PaymentModal from "./PaymentModal";
|
||||
import Plan from "public/plan.svg";
|
||||
import { PriceIds } from "utils/priceList";
|
||||
import { supabase } from "../utils/supabaseClient";
|
||||
import { toast } from "react-toastify";
|
||||
import { useRouter } from "next/router";
|
||||
import Image from 'next/image';
|
||||
import Plan from 'public/plan.svg';
|
||||
import { PriceIds } from 'utils/priceList';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { supabase } from '../utils/supabaseClient';
|
||||
import PaymentModal from './PaymentModal';
|
||||
import Avatar from './Avatar';
|
||||
|
||||
export default function Dashboard(props) {
|
||||
const router = useRouter();
|
||||
@@ -25,7 +25,7 @@ export default function Dashboard(props) {
|
||||
const [payment, setPayment] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (router.query.session_id && router.query.session_id !== "canceled") {
|
||||
if (router.query.session_id && router.query.session_id !== 'canceled') {
|
||||
setPayment(true);
|
||||
}
|
||||
}, []);
|
||||
@@ -43,8 +43,8 @@ export default function Dashboard(props) {
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
let { error } = await supabase.from("profiles").upsert(updates, {
|
||||
returning: "minimal", // Don't return the value after inserting
|
||||
const { error } = await supabase.from('profiles').upsert(updates, {
|
||||
returning: 'minimal', // Don't return the value after inserting
|
||||
});
|
||||
|
||||
if (error) {
|
||||
@@ -54,14 +54,14 @@ export default function Dashboard(props) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
toast.success("Your profile has been updated");
|
||||
toast.success('Your profile has been updated');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col text-left w-full max-w-xl m-auto px-5 py-10'>
|
||||
<div className='max-w-sm flex flex-col justify-center m-auto w-full p-5'>
|
||||
<h1 className='text-4xl font-bold md:text-5xl font-title text-center mb-10'>
|
||||
<div className="flex flex-col text-left w-full max-w-xl m-auto px-5 py-10">
|
||||
<div className="max-w-sm flex flex-col justify-center m-auto w-full p-5">
|
||||
<h1 className="text-4xl font-bold md:text-5xl font-title text-center mb-10">
|
||||
Dashboard
|
||||
</h1>
|
||||
<Avatar
|
||||
@@ -72,60 +72,59 @@ export default function Dashboard(props) {
|
||||
updateProfile({ username, website, avatar_url: url });
|
||||
}}
|
||||
/>
|
||||
<div className='mb-5 flex flex-col'>
|
||||
<label htmlFor='email' className='my-auto text-sm mb-2'>
|
||||
<div className="mb-5 flex flex-col">
|
||||
<label htmlFor="email" className="my-auto text-sm mb-2">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm flex-1 text-base-100'
|
||||
id='email'
|
||||
type='text'
|
||||
className="input input-primary input-bordered input-sm flex-1 text-base-100"
|
||||
id="email"
|
||||
type="text"
|
||||
value={props.session.user.email}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-5 flex flex-col'>
|
||||
<label htmlFor='username' className='my-auto text-sm mb-2'>
|
||||
<div className="mb-5 flex flex-col">
|
||||
<label htmlFor="username" className="my-auto text-sm mb-2">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm flex-1'
|
||||
id='username'
|
||||
type='text'
|
||||
value={username || ""}
|
||||
className="input input-primary input-bordered input-sm flex-1"
|
||||
id="username"
|
||||
type="text"
|
||||
value={username || ''}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-5 flex flex-col'>
|
||||
<label htmlFor='website' className='my-auto text-sm mb-2'>
|
||||
<div className="mb-5 flex flex-col">
|
||||
<label htmlFor="website" className="my-auto text-sm mb-2">
|
||||
Website
|
||||
</label>
|
||||
<input
|
||||
className='input input-primary input-bordered input-sm flex-1'
|
||||
id='website'
|
||||
type='website'
|
||||
value={website || ""}
|
||||
className="input input-primary input-bordered input-sm flex-1"
|
||||
id="website"
|
||||
type="website"
|
||||
value={website || ''}
|
||||
onChange={(e) => setWebsite(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='m-auto'>
|
||||
<div className="m-auto">
|
||||
<button
|
||||
className='btn btn-primary btn-sm'
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={() => updateProfile({ username, website, avatar_url })}
|
||||
disabled={loading}>
|
||||
{loading ? "Loading ..." : "Update My Profile"}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Loading ...' : 'Update My Profile'}
|
||||
</button>
|
||||
</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="max-w-xl flex flex-row flex-wrap m-auto w-full p-5 bordered border-2 border-primary shadow-lg my-5">
|
||||
<Image src={Plan} />
|
||||
<div className='flex flex-col m-auto'>
|
||||
<div className="flex flex-col m-auto">
|
||||
<h2>Your current plan</h2>
|
||||
<p className=''>
|
||||
{props.plan ? PriceIds[props.plan] : "Free tier"}
|
||||
</p>
|
||||
<p className="">{props.plan ? PriceIds[props.plan] : 'Free tier'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<PaymentModal open={payment} setPayment={setPayment} />
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import Link from "next/link";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from 'next/link';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const Footer = () => {
|
||||
const ThemeToggle = dynamic(() => import("components/UI/ThemeToggle.js"), {
|
||||
const ThemeToggle = dynamic(() => import('components/UI/ThemeToggle.js'), {
|
||||
ssr: false,
|
||||
});
|
||||
return (
|
||||
<footer className='w-full flex'>
|
||||
<nav className=' mr-auto'>
|
||||
<div className='flex flex-col sm:flex-row justify-evenly w-full sm:space-x-10'>
|
||||
<div className=''>© {process.env.NEXT_PUBLIC_TITLE}</div>
|
||||
<Link href='/privacy'>
|
||||
<footer className="w-full flex">
|
||||
<nav className=" mr-auto">
|
||||
<div className="flex flex-col sm:flex-row justify-evenly w-full sm:space-x-10">
|
||||
<div className="">© {process.env.NEXT_PUBLIC_TITLE}</div>
|
||||
<Link href="/privacy">
|
||||
<a>Privacy Policy</a>
|
||||
</Link>
|
||||
<Link href='/terms'>
|
||||
<Link href="/terms">
|
||||
<a>Terms of service</a>
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
<div className='mr-5 my-auto'>
|
||||
<div className="mr-5 my-auto">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,67 +1,65 @@
|
||||
import CardsLanding from "components/CardsLanding";
|
||||
import Image from "next/image";
|
||||
import MailingList from "./MailingList";
|
||||
import landTop from "public/landing/land-top.svg";
|
||||
import start from "public/landing/start.svg";
|
||||
import supabaseImage from "public/landing/supabase.svg";
|
||||
import CardsLanding from 'components/CardsLanding';
|
||||
import Image from 'next/image';
|
||||
import landTop from 'public/landing/land-top.svg';
|
||||
import start from 'public/landing/start.svg';
|
||||
import supabaseImage from 'public/landing/supabase.svg';
|
||||
import MailingList from './MailingList';
|
||||
|
||||
const Landing = () => {
|
||||
return (
|
||||
<div className='mt-10 mb-20 text-base-content w-full'>
|
||||
<div className='flex max-w-6xl m-auto justify-around'>
|
||||
<div className='max-w-sm mr-16 my-auto'>
|
||||
<h2 className='text-4xl font-bold font-title text-left leading-normal'>
|
||||
Build your <span className='text-primary'>SaaS</span> in the blink
|
||||
of an eye!
|
||||
</h2>
|
||||
<p>
|
||||
SupaNexTail got your back, and takes care of the initial setup,
|
||||
sometimes time consuming, but essential to your success.
|
||||
</p>
|
||||
</div>
|
||||
<div className='max-w-xl'>
|
||||
<Image src={landTop} height={417} width={583} />
|
||||
</div>
|
||||
const Landing = () => (
|
||||
<div className="mt-10 mb-20 text-base-content w-full">
|
||||
<div className="flex max-w-6xl m-auto justify-around">
|
||||
<div className="max-w-sm mr-16 my-auto">
|
||||
<h2 className="text-4xl font-bold font-title text-left leading-normal">
|
||||
Build your <span className="text-primary">SaaS</span> in the blink of
|
||||
an eye!
|
||||
</h2>
|
||||
<p>
|
||||
SupaNexTail got your back, and takes care of the initial setup,
|
||||
sometimes time consuming, but essential to your success.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CardsLanding />
|
||||
<div className='flex max-w-6xl m-auto justify-around mt-14 flex-wrap'>
|
||||
<div className='max-w-sm mr-16 my-auto'>
|
||||
<h2 className='text-4xl font-bold font-title text-left leading-normal'>
|
||||
All you need to start <span className='text-primary'>now</span>
|
||||
</h2>
|
||||
<p>
|
||||
SupaNexTail got your back, and takes care of the initial setup,
|
||||
sometimes time consuming, but essential to your success.
|
||||
</p>
|
||||
</div>
|
||||
<div className='max-w-xl'>
|
||||
<Image src={start} />
|
||||
</div>
|
||||
<div className="max-w-xl">
|
||||
<Image src={landTop} height={417} width={583} />
|
||||
</div>
|
||||
<div className='flex max-w-6xl m-auto justify-around mt-24 flex-wrap'>
|
||||
<div className='max-w-md my-auto order-1 lg:order-2'>
|
||||
<h2 className='text-4xl font-bold font-title text-left leading-normal'>
|
||||
Leverage the power of <span className='text-primary'>Supabase</span>
|
||||
</h2>
|
||||
<p>
|
||||
Supabase is an open source Firebase alternative. You’ll have a
|
||||
database, an auth system, a storage system, and much more in one
|
||||
product.
|
||||
</p>
|
||||
<p>
|
||||
SupaNexTail uses Supabase at its core, and preconfigures all the
|
||||
useful elements for your site. User registration, synchronization
|
||||
with Stripe, we’ve got you covered!
|
||||
</p>
|
||||
</div>
|
||||
<div className='max-w-md order-2 lg:order-1 flex'>
|
||||
<Image src={supabaseImage} />
|
||||
</div>
|
||||
</div>
|
||||
<MailingList />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
<CardsLanding />
|
||||
<div className="flex max-w-6xl m-auto justify-around mt-14 flex-wrap">
|
||||
<div className="max-w-sm mr-16 my-auto">
|
||||
<h2 className="text-4xl font-bold font-title text-left leading-normal">
|
||||
All you need to start <span className="text-primary">now</span>
|
||||
</h2>
|
||||
<p>
|
||||
SupaNexTail got your back, and takes care of the initial setup,
|
||||
sometimes time consuming, but essential to your success.
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-w-xl">
|
||||
<Image src={start} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex max-w-6xl m-auto justify-around mt-24 flex-wrap">
|
||||
<div className="max-w-md my-auto order-1 lg:order-2">
|
||||
<h2 className="text-4xl font-bold font-title text-left leading-normal">
|
||||
Leverage the power of <span className="text-primary">Supabase</span>
|
||||
</h2>
|
||||
<p>
|
||||
Supabase is an open source Firebase alternative. You’ll have a
|
||||
database, an auth system, a storage system, and much more in one
|
||||
product.
|
||||
</p>
|
||||
<p>
|
||||
SupaNexTail uses Supabase at its core, and preconfigures all the
|
||||
useful elements for your site. User registration, synchronization with
|
||||
Stripe, we’ve got you covered!
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-w-md order-2 lg:order-1 flex">
|
||||
<Image src={supabaseImage} />
|
||||
</div>
|
||||
</div>
|
||||
<MailingList />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Landing;
|
||||
|
||||
@@ -10,59 +10,60 @@ You also have the head component containing all the favicon for different platfo
|
||||
The images are in the public folder.
|
||||
*/
|
||||
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import Footer from "./Footer";
|
||||
import Head from "next/head";
|
||||
import Nav from "./Nav";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import { useAuth } from "utils/AuthContext";
|
||||
import Head from 'next/head';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { useAuth } from 'utils/AuthContext';
|
||||
import Nav from './Nav';
|
||||
import Footer from './Footer';
|
||||
|
||||
const Layout = (props) => {
|
||||
const { user, signOut } = useAuth();
|
||||
|
||||
const toastStyle = {
|
||||
//Style your toast elements here
|
||||
success: "bg-accent",
|
||||
error: "bg-red-600",
|
||||
info: "bg-gray-600",
|
||||
warning: "bg-orange-400",
|
||||
default: "bg-primary",
|
||||
dark: "bg-white-600 font-gray-300",
|
||||
// Style your toast elements here
|
||||
success: 'bg-accent',
|
||||
error: 'bg-red-600',
|
||||
info: 'bg-gray-600',
|
||||
warning: 'bg-orange-400',
|
||||
default: 'bg-primary',
|
||||
dark: 'bg-white-600 font-gray-300',
|
||||
};
|
||||
return (
|
||||
<div className='min-h-screen w-full bg-base-100 text-base-content m-auto font-body'>
|
||||
<div className="min-h-screen w-full bg-base-100 text-base-content m-auto font-body">
|
||||
<Head>
|
||||
<link
|
||||
rel='apple-touch-icon'
|
||||
sizes='180x180'
|
||||
href='/apple-touch-icon.png'
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='32x32'
|
||||
href='/favicon-32x32.png'
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='16x16'
|
||||
href='/favicon-16x16.png'
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/favicon-16x16.png"
|
||||
/>
|
||||
<link rel='manifest' href='/site.webmanifest' />
|
||||
<link rel='mask-icon' href='/safari-pinned-tab.svg' color='#5bbad5' />
|
||||
<meta name='msapplication-TileColor' content='#da532c' />
|
||||
<meta name='theme-color' content='#ffffff' />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
</Head>
|
||||
<div className='max-w-7xl flex flex-col min-h-screen mx-auto p-5'>
|
||||
<div className="max-w-7xl flex flex-col min-h-screen mx-auto p-5">
|
||||
<Nav user={user} signOut={signOut} />
|
||||
<main className='flex-1'>{props.children}</main>
|
||||
<main className="flex-1">{props.children}</main>
|
||||
<ToastContainer
|
||||
position='bottom-center'
|
||||
position="bottom-center"
|
||||
toastClassName={({ type }) =>
|
||||
toastStyle[type || "default"] +
|
||||
" flex p-5 my-5 min-h-10 rounded-md justify-between overflow-hidden cursor-pointer "
|
||||
`${
|
||||
toastStyle[type || 'default']
|
||||
} flex p-5 my-5 min-h-10 rounded-md justify-between overflow-hidden cursor-pointer `
|
||||
}
|
||||
/>
|
||||
<Footer />
|
||||
|
||||
@@ -3,11 +3,11 @@ This is the form component to register an email adress to your mailing list.
|
||||
This is just the frontend, and the email will be send to our backend API (/api/mailingList)
|
||||
*/
|
||||
|
||||
import Image from "next/image";
|
||||
import Mailing from "public/landing/mailing.svg";
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import { useState } from "react";
|
||||
import Image from 'next/image';
|
||||
import Mailing from 'public/landing/mailing.svg';
|
||||
import axios from 'axios';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useState } from 'react';
|
||||
|
||||
const MailingList = () => {
|
||||
const [mail, setMail] = useState(null);
|
||||
@@ -16,7 +16,7 @@ const MailingList = () => {
|
||||
|
||||
const validateEmail = () => {
|
||||
// Regex patern for email validation
|
||||
let regex =
|
||||
const regex =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
||||
if (regex.test(mail)) {
|
||||
@@ -25,7 +25,7 @@ const MailingList = () => {
|
||||
setValid(true);
|
||||
} else {
|
||||
// invalid email.
|
||||
toast.error("Your email is invalid");
|
||||
toast.error('Your email is invalid');
|
||||
setValid(false);
|
||||
}
|
||||
};
|
||||
@@ -33,7 +33,7 @@ const MailingList = () => {
|
||||
const subscribe = () => {
|
||||
setLoading(true);
|
||||
axios
|
||||
.put("api/mailingList", {
|
||||
.put('api/mailingList', {
|
||||
mail,
|
||||
})
|
||||
.then((result) => {
|
||||
@@ -48,32 +48,34 @@ const MailingList = () => {
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div className='my-10 mt-24 m-auto flex flex-col'>
|
||||
<h2 className='text-3xl md:text-4xl font-bold font-title uppercase text-center'>
|
||||
<div className="my-10 mt-24 m-auto flex flex-col">
|
||||
<h2 className="text-3xl md:text-4xl font-bold font-title uppercase text-center">
|
||||
Stay Tuned
|
||||
</h2>
|
||||
<Image src={Mailing}/>
|
||||
<label className='label'>
|
||||
<p className='text-center max-w-md m-auto'>
|
||||
<Image src={Mailing} />
|
||||
<label className="label">
|
||||
<p className="text-center max-w-md m-auto">
|
||||
Want to be the first to know when SupaNexTail launches and get an
|
||||
exclusive discount? Sign up for the newsletter!
|
||||
</p>
|
||||
</label>
|
||||
<div className='mt-5 m-auto'>
|
||||
<div className="mt-5 m-auto">
|
||||
<input
|
||||
onChange={(e) => {
|
||||
setMail(e.target.value);
|
||||
}}
|
||||
type='email'
|
||||
placeholder='Your email'
|
||||
type="email"
|
||||
placeholder="Your email"
|
||||
className={`input input-primary input-bordered ${
|
||||
valid ? null : "input-error"
|
||||
}`}></input>
|
||||
valid ? null : 'input-error'
|
||||
}`}
|
||||
/>
|
||||
<button
|
||||
onClick={validateEmail}
|
||||
className={`btn ml-3 ${
|
||||
loading ? "btn-disabled loading" : "btn-primary"
|
||||
}`}>
|
||||
loading ? 'btn-disabled loading' : 'btn-primary'
|
||||
}`}
|
||||
>
|
||||
I'm in!
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2,42 +2,42 @@
|
||||
This is your Nav component. It contain a responsive navbar
|
||||
*/
|
||||
|
||||
import { LogOut, Menu } from "react-feather";
|
||||
import { LogOut, Menu } from 'react-feather';
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import Logo from "public/logo.svg";
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import Logo from 'public/logo.svg';
|
||||
|
||||
const Nav = (props) => {
|
||||
//Modify you menu directly here
|
||||
// Modify you menu directly here
|
||||
const NavMenu = (
|
||||
<>
|
||||
{props.user && (
|
||||
<Link href='/dashboard'>
|
||||
<a className='nav-btn'>Dashboard</a>
|
||||
<Link href="/dashboard">
|
||||
<a className="nav-btn">Dashboard</a>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Link href='/pricing'>
|
||||
<a className='nav-btn'>Pricing</a>
|
||||
<Link href="/pricing">
|
||||
<a className="nav-btn">Pricing</a>
|
||||
</Link>
|
||||
|
||||
<Link href='/contact'>
|
||||
<a className='nav-btn'>Contact Us</a>
|
||||
<Link href="/contact">
|
||||
<a className="nav-btn">Contact Us</a>
|
||||
</Link>
|
||||
|
||||
{props.user ? (
|
||||
<button className='btn btn-xs text-xs' onClick={() => props.signOut()}>
|
||||
<LogOut size={12} className='mr-2' />
|
||||
<button className="btn btn-xs text-xs" onClick={() => props.signOut()}>
|
||||
<LogOut size={12} className="mr-2" />
|
||||
Logout
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<Link href='/login'>
|
||||
<a className='nav-btn'>Login</a>
|
||||
<Link href="/login">
|
||||
<a className="nav-btn">Login</a>
|
||||
</Link>
|
||||
<Link href='/signup'>
|
||||
<a className='btn btn-sm btn-primary font-body normal-case font-normal'>
|
||||
<Link href="/signup">
|
||||
<a className="btn btn-sm btn-primary font-body normal-case font-normal">
|
||||
Sign Up
|
||||
</a>
|
||||
</Link>
|
||||
@@ -47,22 +47,22 @@ const Nav = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<nav className='navbar mb-2 w-full'>
|
||||
<Link href='/'>
|
||||
<nav className="navbar mb-2 w-full">
|
||||
<Link href="/">
|
||||
<a>
|
||||
<Image src={Logo} />
|
||||
</a>
|
||||
</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="hidden lg:flex text-center flex-col lg:flex-row lg:space-x-10 font-body text-sm ml-auto">
|
||||
{NavMenu}
|
||||
</div>
|
||||
<div className='ml-auto lg:hidden'>
|
||||
<div className='dropdown dropdown-end' data-cy='dropdown'>
|
||||
<div tabIndex='0' className='m-1 cursor-pointer'>
|
||||
<div className="ml-auto lg:hidden">
|
||||
<div className="dropdown dropdown-end" data-cy="dropdown">
|
||||
<div tabIndex="0" className="m-1 cursor-pointer">
|
||||
<Menu />
|
||||
</div>
|
||||
<div className='menu dropdown-content mt-3 text-center space-y-3 w-24'>
|
||||
<div className="menu dropdown-content mt-3 text-center space-y-3 w-24">
|
||||
{NavMenu}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
|
||||
import { Fragment } from "react";
|
||||
import { Fragment } from 'react';
|
||||
|
||||
const PaymentModal = (props) => {
|
||||
function closeModal() {
|
||||
@@ -11,53 +11,59 @@ const PaymentModal = (props) => {
|
||||
<>
|
||||
<Transition appear show={props.open} as={Fragment}>
|
||||
<Dialog
|
||||
as='div'
|
||||
className='fixed inset-0 z-10 overflow-y-auto bg-gray-500 bg-opacity-50'
|
||||
onClose={closeModal}>
|
||||
<div className='min-h-screen px-4 text-center'>
|
||||
as="div"
|
||||
className="fixed inset-0 z-10 overflow-y-auto bg-gray-500 bg-opacity-50"
|
||||
onClose={closeModal}
|
||||
>
|
||||
<div className="min-h-screen px-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'>
|
||||
<Dialog.Overlay className='fixed inset-0' />
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0" />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span
|
||||
className='inline-block h-screen align-middle'
|
||||
aria-hidden='true'>
|
||||
className="inline-block h-screen align-middle"
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'>
|
||||
<div className='inline-block w-full max-w-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'>
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="inline-block w-full max-w-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
|
||||
as='h3'
|
||||
className='text-2xl font-bold leading-6 mb-5 text-center'>
|
||||
as="h3"
|
||||
className="text-2xl font-bold leading-6 mb-5 text-center"
|
||||
>
|
||||
Payment successful 🎉
|
||||
</Dialog.Title>
|
||||
<div className='mt-2'>
|
||||
<div className="mt-2">
|
||||
<p>
|
||||
Your payment has been successfully submitted. Thank you for
|
||||
your support!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='mt-4'>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-accent flex m-auto'
|
||||
onClick={closeModal}>
|
||||
type="button"
|
||||
className="btn btn-accent flex m-auto"
|
||||
onClick={closeModal}
|
||||
>
|
||||
Got it, thanks!
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -6,14 +6,14 @@ Dont forget to create your customer portal on Stripe
|
||||
https://dashboard.stripe.com/test/settings/billing/portal
|
||||
*/
|
||||
|
||||
import { getSub, supabase } from "utils/supabaseClient";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getSub, supabase } from 'utils/supabaseClient';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Prices } from "utils/priceList";
|
||||
import { Switch } from "@headlessui/react";
|
||||
import axios from "axios";
|
||||
import router from "next/router";
|
||||
import { useAuth } from "utils/AuthContext";
|
||||
import { Prices } from 'utils/priceList';
|
||||
import { Switch } from '@headlessui/react';
|
||||
import axios from 'axios';
|
||||
import router from 'next/router';
|
||||
import { useAuth } from 'utils/AuthContext';
|
||||
|
||||
const Pricing = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
@@ -24,8 +24,8 @@ const Pricing = () => {
|
||||
|
||||
const portal = () => {
|
||||
axios
|
||||
.post("/api/stripe/customer-portal", {
|
||||
customerId: customerId,
|
||||
.post('/api/stripe/customer-portal', {
|
||||
customerId,
|
||||
})
|
||||
.then((result) => {
|
||||
router.push(result.data.url);
|
||||
@@ -36,9 +36,9 @@ const Pricing = () => {
|
||||
if (user) {
|
||||
getSub().then((result) => setSub(result));
|
||||
supabase
|
||||
.from("subscriptions")
|
||||
.from('subscriptions')
|
||||
.select(`customer_id`)
|
||||
.eq("id", user.id)
|
||||
.eq('id', user.id)
|
||||
.single()
|
||||
.then((result) => {
|
||||
setCustomerId(result.data?.customer_id);
|
||||
@@ -48,19 +48,19 @@ const Pricing = () => {
|
||||
|
||||
const pricing = {
|
||||
monthly: {
|
||||
personal: "$5/mo",
|
||||
team: "$15/mo",
|
||||
pro: "$35/mo",
|
||||
personal: '$5/mo',
|
||||
team: '$15/mo',
|
||||
pro: '$35/mo',
|
||||
},
|
||||
yearly: {
|
||||
personal: "$50/yr",
|
||||
team: "$150/yr",
|
||||
pro: "$350/yr",
|
||||
personal: '$50/yr',
|
||||
team: '$150/yr',
|
||||
pro: '$350/yr',
|
||||
},
|
||||
flat: {
|
||||
personal: "€49",
|
||||
team: "€99",
|
||||
pro: "€149",
|
||||
personal: '€49',
|
||||
team: '€99',
|
||||
pro: '€149',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -68,31 +68,31 @@ const Pricing = () => {
|
||||
e.preventDefault();
|
||||
// Create a Checkout Session. This will redirect the user to the Stripe website for the payment.
|
||||
axios
|
||||
.post("/api/stripe/create-checkout-session", {
|
||||
priceId: priceId,
|
||||
.post('/api/stripe/create-checkout-session', {
|
||||
priceId,
|
||||
email: user.email,
|
||||
customerId: customerId,
|
||||
customerId,
|
||||
userId: user.id,
|
||||
tokenId: session.access_token,
|
||||
pay_mode: flat ? "payment" : "subscription",
|
||||
pay_mode: flat ? 'payment' : 'subscription',
|
||||
})
|
||||
.then((result) => router.push(result.data.url));
|
||||
};
|
||||
return (
|
||||
<div className='w-full mx-auto px-5 py-10 mb-10'>
|
||||
<div className='text-center max-w-xl mx-auto'>
|
||||
<h1 className='text-3xl sm:text-5xl font-bold font-title mb-5'>
|
||||
<div className="w-full mx-auto px-5 py-10 mb-10">
|
||||
<div className="text-center max-w-xl mx-auto">
|
||||
<h1 className="text-3xl sm:text-5xl font-bold font-title mb-5">
|
||||
Pricing
|
||||
</h1>
|
||||
<h3 className='text-lg font-light leading-8 p-3 mb-5'>
|
||||
<h3 className="text-lg font-light leading-8 p-3 mb-5">
|
||||
This is an example of a pricing page. You can choose a payment method,
|
||||
monthly or yearly.
|
||||
</h3>
|
||||
</div>
|
||||
{!flat && (
|
||||
<div className='flex justify-between max-w-xs m-auto mb-3'>
|
||||
<div className="flex justify-between max-w-xs m-auto mb-3">
|
||||
<div>
|
||||
<p className={`${enabled ? "text-gray-500" : null}`}>
|
||||
<p className={`${enabled ? 'text-gray-500' : null}`}>
|
||||
Billed monthly
|
||||
</p>
|
||||
</div>
|
||||
@@ -103,45 +103,46 @@ const Pricing = () => {
|
||||
className={`bg-primary relative inline-flex flex-shrink-0 h-[38px] w-[74px]
|
||||
border-2 border-transparent rounded-full cursor-pointer transition-colors
|
||||
ease-in-out duration-200 focus:outline-none focus-visible:ring-2
|
||||
focus-visible:ring-white focus-visible:ring-opacity-75`}>
|
||||
<span className='sr-only'>Switch bill</span>
|
||||
focus-visible:ring-white focus-visible:ring-opacity-75`}
|
||||
>
|
||||
<span className="sr-only">Switch bill</span>
|
||||
<span
|
||||
aria-hidden='true'
|
||||
className={`${enabled ? "translate-x-9" : "translate-x-0"}
|
||||
aria-hidden="true"
|
||||
className={`${enabled ? 'translate-x-9' : 'translate-x-0'}
|
||||
pointer-events-none inline-block h-[34px] w-[34px] rounded-full bg-white shadow-lg transform ring-0 transition ease-in-out duration-200`}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
<div>
|
||||
<p className={`${!enabled ? "text-gray-500" : null}`}>
|
||||
<p className={`${!enabled ? 'text-gray-500' : null}`}>
|
||||
Billed annually
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className='max-w-4xl mx-auto md:flex space-x-4'>
|
||||
<div className='w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Personal</h2>
|
||||
<h3 className='text-center font-bold text-4xl mb-5'>
|
||||
<div className="max-w-4xl mx-auto md:flex space-x-4">
|
||||
<div className="w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col">
|
||||
<div className="w-full flex-grow">
|
||||
<h2 className="text-center font-bold uppercase mb-4">Personal</h2>
|
||||
<h3 className="text-center font-bold text-4xl mb-5">
|
||||
{flat
|
||||
? pricing.flat.personal
|
||||
: enabled
|
||||
? pricing.yearly.personal
|
||||
: pricing.monthly.personal}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8 text-left'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> A cool feature
|
||||
<ul className="text-sm px-5 mb-8 text-left">
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> A cool feature
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Another feature
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Another feature
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className="w-full">
|
||||
<button
|
||||
className='btn btn-primary w-full'
|
||||
className="btn btn-primary w-full"
|
||||
onClick={
|
||||
user
|
||||
? sub
|
||||
@@ -156,45 +157,45 @@ const Pricing = () => {
|
||||
: Prices.personal.monthly.id
|
||||
)
|
||||
: () => {
|
||||
router.push("/auth");
|
||||
router.push('/auth');
|
||||
}
|
||||
}>
|
||||
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
|
||||
}
|
||||
>
|
||||
{user ? (sub ? 'Upgrade' : 'Buy Now') : 'Register'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Team</h2>
|
||||
<h3 className='text-center font-bold text-4xl mb-5'>
|
||||
<div className="w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col">
|
||||
<div className="w-full flex-grow">
|
||||
<h2 className="text-center font-bold uppercase mb-4">Team</h2>
|
||||
<h3 className="text-center font-bold text-4xl mb-5">
|
||||
{flat
|
||||
? pricing.flat.team
|
||||
: enabled
|
||||
? pricing.yearly.team
|
||||
: pricing.monthly.team}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8 text-left'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> All basic
|
||||
features
|
||||
<ul className="text-sm px-5 mb-8 text-left">
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> All basic features
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Dolor sit amet
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Dolor sit amet
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Consectetur
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Consectetur
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Adipisicing
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Adipisicing
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Elit repellat
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Elit repellat
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className="w-full">
|
||||
<button
|
||||
className='btn btn-primary w-full'
|
||||
className="btn btn-primary w-full"
|
||||
onClick={
|
||||
user
|
||||
? sub
|
||||
@@ -209,44 +210,45 @@ const Pricing = () => {
|
||||
: Prices.team.monthly.id
|
||||
)
|
||||
: () => {
|
||||
router.push("/auth");
|
||||
router.push('/auth');
|
||||
}
|
||||
}>
|
||||
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
|
||||
}
|
||||
>
|
||||
{user ? (sub ? 'Upgrade' : 'Buy Now') : 'Register'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col'>
|
||||
<div className='w-full flex-grow'>
|
||||
<h2 className='text-center font-bold uppercase mb-4'>Pro</h2>
|
||||
<h3 className='text-center font-bold text-4xl mb-5'>
|
||||
<div className="w-full md:w-1/3 md:max-w-none px-8 md:px-10 py-8 md:py-10 mb-3 mx-auto md:my-6 rounded-md shadow-lg shadow-gray-600 md:flex md:flex-col">
|
||||
<div className="w-full flex-grow">
|
||||
<h2 className="text-center font-bold uppercase mb-4">Pro</h2>
|
||||
<h3 className="text-center font-bold text-4xl mb-5">
|
||||
{flat
|
||||
? pricing.flat.pro
|
||||
: enabled
|
||||
? pricing.yearly.pro
|
||||
: pricing.monthly.pro}
|
||||
</h3>
|
||||
<ul className='text-sm px-5 mb-8 text-left'>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Lorem ipsum
|
||||
<ul className="text-sm px-5 mb-8 text-left">
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Lorem ipsum
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Dolor sit amet
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Dolor sit amet
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Consectetur
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Consectetur
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Adipisicing
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Adipisicing
|
||||
</li>
|
||||
<li className='leading-tight'>
|
||||
<i className='mdi mdi-check-bold text-lg'></i> Much more...
|
||||
<li className="leading-tight">
|
||||
<i className="mdi mdi-check-bold text-lg" /> Much more...
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className="w-full">
|
||||
<button
|
||||
className='btn btn-primary w-full'
|
||||
className="btn btn-primary w-full"
|
||||
onClick={
|
||||
user
|
||||
? sub
|
||||
@@ -261,10 +263,11 @@ const Pricing = () => {
|
||||
: Prices.pro.monthly.id
|
||||
)
|
||||
: () => {
|
||||
router.push("/auth");
|
||||
router.push('/auth');
|
||||
}
|
||||
}>
|
||||
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
|
||||
}
|
||||
>
|
||||
{user ? (sub ? 'Upgrade' : 'Buy Now') : 'Register'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,163 +1,159 @@
|
||||
const PrivacyPolicy = () => {
|
||||
return (
|
||||
<div className='max-w-xl text-left m-auto py-10'>
|
||||
<h1 className='text-center'>
|
||||
Privacy Policy for {process.env.NEXT_PUBLIC_TITLE}
|
||||
</h1>
|
||||
const PrivacyPolicy = () => (
|
||||
<div className="max-w-xl text-left m-auto py-10">
|
||||
<h1 className="text-center">
|
||||
Privacy Policy for {process.env.NEXT_PUBLIC_TITLE}
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
At {process.env.NEXT_PUBLIC_TITLE}, accessible from
|
||||
https://www.supanextail.dev, one of our main priorities is the privacy
|
||||
of 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.
|
||||
</p>
|
||||
<p>
|
||||
At {process.env.NEXT_PUBLIC_TITLE}, accessible from
|
||||
https://www.supanextail.dev, one of our main priorities is the privacy of
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have additional questions or require more information about our
|
||||
Privacy Policy, do not hesitate to contact us.
|
||||
</p>
|
||||
<p>
|
||||
If you have additional questions or require more information about our
|
||||
Privacy Policy, do not hesitate to contact us.
|
||||
</p>
|
||||
|
||||
<h2>General Data Protection Regulation (GDPR)</h2>
|
||||
<p>We are a Data Controller of your information.</p>
|
||||
<h2>General Data Protection Regulation (GDPR)</h2>
|
||||
<p>We are a Data Controller of your information.</p>
|
||||
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} legal basis for collecting and using the
|
||||
personal information described in this Privacy Policy depends on the
|
||||
Personal Information we collect and the specific context in which we
|
||||
collect the information:
|
||||
</p>
|
||||
<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>
|
||||
Processing your personal information is in{" "}
|
||||
{process.env.NEXT_PUBLIC_TITLE} legitimate interests
|
||||
</li>
|
||||
<li>{process.env.NEXT_PUBLIC_TITLE} needs to comply with the law</li>
|
||||
</ul>
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} legal basis for collecting and using the
|
||||
personal information described in this Privacy Policy depends on the
|
||||
Personal Information we collect and the specific context in which we
|
||||
collect the information:
|
||||
</p>
|
||||
<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>
|
||||
Processing your personal information is in{' '}
|
||||
{process.env.NEXT_PUBLIC_TITLE} legitimate interests
|
||||
</li>
|
||||
<li>{process.env.NEXT_PUBLIC_TITLE} needs to comply with the law</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} will retain your personal information
|
||||
only for as long as is necessary for the purposes set out in this
|
||||
Privacy Policy. We will retain and use your information to the extent
|
||||
necessary to comply with our legal obligations, resolve disputes, and
|
||||
enforce our policies.
|
||||
</p>
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} will retain your personal information only
|
||||
for as long as is necessary for the purposes set out in this Privacy
|
||||
Policy. We will retain and use your information to the extent necessary to
|
||||
comply with our legal obligations, resolve disputes, and enforce our
|
||||
policies.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are a resident of the European Economic Area (EEA), you have
|
||||
certain data protection rights. If you wish to be informed what Personal
|
||||
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>
|
||||
<ul>
|
||||
<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 to object.</li>
|
||||
<li>The right of restriction.</li>
|
||||
<li>The right to data portability</li>
|
||||
<li>The right to withdraw consent</li>
|
||||
</ul>
|
||||
<p>
|
||||
If you are a resident of the European Economic Area (EEA), you have
|
||||
certain data protection rights. If you wish to be informed what Personal
|
||||
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>
|
||||
<ul>
|
||||
<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 to object.</li>
|
||||
<li>The right of restriction.</li>
|
||||
<li>The right to data portability</li>
|
||||
<li>The right to withdraw consent</li>
|
||||
</ul>
|
||||
|
||||
<h2>Log Files</h2>
|
||||
<h2>Log Files</h2>
|
||||
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} follows a standard procedure of using
|
||||
log files. These files log visitors when they visit websites. All
|
||||
hosting companies do this and a part of hosting services' analytics. The
|
||||
information collected by log files include internet protocol (IP)
|
||||
addresses, browser type, Internet Service Provider (ISP), date and time
|
||||
stamp, referring/exit pages, and possibly the number of clicks. These
|
||||
are 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>
|
||||
{process.env.NEXT_PUBLIC_TITLE} follows a standard procedure of using log
|
||||
files. These files log visitors when they visit websites. All hosting
|
||||
companies do this and a part of hosting services' analytics. The
|
||||
information collected by log files include internet protocol (IP)
|
||||
addresses, browser type, Internet Service Provider (ISP), date and time
|
||||
stamp, referring/exit pages, and possibly the number of clicks. These are
|
||||
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>
|
||||
|
||||
<h2>Privacy Policies</h2>
|
||||
<h2>Privacy Policies</h2>
|
||||
|
||||
<p>
|
||||
You may consult this list to find the Privacy Policy for each of the
|
||||
advertising partners of {process.env.NEXT_PUBLIC_TITLE}.
|
||||
</p>
|
||||
<p>
|
||||
You may consult this list to find the Privacy Policy for each of the
|
||||
advertising partners of {process.env.NEXT_PUBLIC_TITLE}.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Third-party ad servers or ad networks uses technologies like cookies,
|
||||
JavaScript, or Web Beacons that are used in their respective
|
||||
advertisements and links that appear on {process.env.NEXT_PUBLIC_TITLE},
|
||||
which are sent directly to users' browser. They automatically receive
|
||||
your IP address when this occurs. These technologies are used to measure
|
||||
the effectiveness of their advertising campaigns and/or to personalize
|
||||
the advertising content that you see on websites that you visit.
|
||||
</p>
|
||||
<p>
|
||||
Third-party ad servers or ad networks uses technologies like cookies,
|
||||
JavaScript, or Web Beacons that are used in their respective
|
||||
advertisements and links that appear on {process.env.NEXT_PUBLIC_TITLE},
|
||||
which are sent directly to users' browser. They automatically receive your
|
||||
IP address when this occurs. These technologies are used to measure the
|
||||
effectiveness of their advertising campaigns and/or to personalize the
|
||||
advertising content that you see on websites that you visit.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that {process.env.NEXT_PUBLIC_TITLE} has no access to or control
|
||||
over these cookies that are used by third-party advertisers.
|
||||
</p>
|
||||
<p>
|
||||
Note that {process.env.NEXT_PUBLIC_TITLE} has no access to or control over
|
||||
these cookies that are used by third-party advertisers.
|
||||
</p>
|
||||
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE}'s Privacy Policy does not apply to other
|
||||
advertisers or websites. Thus, we are advising you to consult the
|
||||
respective Privacy Policies of these third-party ad servers for more
|
||||
detailed information. It may include their practices and instructions
|
||||
about how to opt-out of certain options.{" "}
|
||||
</p>
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE}'s Privacy Policy does not apply to other
|
||||
advertisers or websites. Thus, we are advising you to consult the
|
||||
respective Privacy Policies of these third-party ad servers for more
|
||||
detailed information. It may include their practices and instructions
|
||||
about how to opt-out of certain options.{' '}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can choose to disable cookies through your individual browser
|
||||
options. To know more detailed information about cookie management with
|
||||
specific web browsers, it can be found at the browsers' respective
|
||||
websites.
|
||||
</p>
|
||||
<p>
|
||||
You can choose to disable cookies through your individual browser options.
|
||||
To know more detailed information about cookie management with specific
|
||||
web browsers, it can be found at the browsers' respective websites.
|
||||
</p>
|
||||
|
||||
<h2>Children's Information</h2>
|
||||
<h2>Children's Information</h2>
|
||||
|
||||
<p>
|
||||
Another part of our priority is adding protection for children while
|
||||
using the internet. We encourage parents and guardians to observe,
|
||||
participate in, and/or monitor and guide their online activity.
|
||||
</p>
|
||||
<p>
|
||||
Another part of our priority is adding protection for children while using
|
||||
the internet. We encourage parents and guardians to observe, participate
|
||||
in, and/or monitor and guide their online activity.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} does not knowingly collect any Personal
|
||||
Identifiable Information from children under the age of 13. If you think
|
||||
that your child provided this kind of information on our website, we
|
||||
strongly encourage you to contact us immediately and we will do our best
|
||||
efforts to promptly remove such information from our records.
|
||||
</p>
|
||||
<p>
|
||||
{process.env.NEXT_PUBLIC_TITLE} does not knowingly collect any Personal
|
||||
Identifiable Information from children under the age of 13. If you think
|
||||
that your child provided this kind of information on our website, we
|
||||
strongly encourage you to contact us immediately and we will do our best
|
||||
efforts to promptly remove such information from our records.
|
||||
</p>
|
||||
|
||||
<h2>Online Privacy Policy Only</h2>
|
||||
<h2>Online Privacy Policy Only</h2>
|
||||
|
||||
<p>
|
||||
Our Privacy Policy applies only to our online activities and is valid
|
||||
for visitors to our website with regards to the information that they
|
||||
shared and/or collect in {process.env.NEXT_PUBLIC_TITLE}. This policy is
|
||||
not applicable to any information collected offline or via channels
|
||||
other than this website.
|
||||
</p>
|
||||
<p>
|
||||
Our Privacy Policy applies only to our online activities and is valid for
|
||||
visitors to our website with regards to the information that they shared
|
||||
and/or collect in {process.env.NEXT_PUBLIC_TITLE}. This policy is not
|
||||
applicable to any information collected offline or via channels other than
|
||||
this website.
|
||||
</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>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<p>
|
||||
By using our website, you hereby consent to our Privacy Policy and agree
|
||||
to its terms.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PrivacyPolicy;
|
||||
|
||||
@@ -7,17 +7,17 @@ You can select your auth providers, or just keep the email/password. You can
|
||||
check the providers available here: https://supabase.io/docs/guides/auth
|
||||
*/
|
||||
|
||||
import SignUpPanel from "./UI/SignUpPanel";
|
||||
import { supabase } from "utils/supabaseClient";
|
||||
import { useAuth } from "utils/AuthContext";
|
||||
import { supabase } from 'utils/supabaseClient';
|
||||
import { useAuth } from 'utils/AuthContext';
|
||||
import SignUpPanel from './UI/SignUpPanel';
|
||||
|
||||
const Container = (props) => {
|
||||
const { user, signOut } = useAuth();
|
||||
if (user)
|
||||
return (
|
||||
<div className='w-80 md:w-96 order-first lg:order-last'>
|
||||
<div className="w-80 md:w-96 order-first lg:order-last">
|
||||
<p>Hello {user.email}! 👋 You are already logged in</p>
|
||||
<button className='btn btn-primary' onClick={() => signOut()}>
|
||||
<button className="btn btn-primary" onClick={() => signOut()}>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,239 +1,235 @@
|
||||
const Terms = () => {
|
||||
return (
|
||||
<div className='max-w-xl text-left m-auto py-10'>
|
||||
<h1>Terms and Conditions</h1>
|
||||
const Terms = () => (
|
||||
<div className="max-w-xl text-left m-auto py-10">
|
||||
<h1>Terms and Conditions</h1>
|
||||
|
||||
<p>
|
||||
The following terms and conditions (collectively, these "Terms and
|
||||
Conditions") apply to your use of{" "}
|
||||
<span className='website_url'>https://www.supanextail.dev</span>,
|
||||
including any content, functionality and services offered on or via{" "}
|
||||
<span className='website_url'>https://www.supanextail.dev</span> (the
|
||||
"Website").
|
||||
</p>
|
||||
<p>
|
||||
The following terms and conditions (collectively, these "Terms and
|
||||
Conditions") apply to your use of{' '}
|
||||
<span className="website_url">https://www.supanextail.dev</span>,
|
||||
including any content, functionality and services offered on or via{' '}
|
||||
<span className="website_url">https://www.supanextail.dev</span> (the
|
||||
"Website").
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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 Website you accept and agree to be bound and abide
|
||||
by these Terms and Conditions.
|
||||
</p>
|
||||
<p>
|
||||
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 Website you accept and agree to be bound and abide by
|
||||
these Terms and Conditions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
These Terms and Conditions are effective as of{" "}
|
||||
<span className='date'>06/22/2021</span>. We expressly reserve the right to
|
||||
change these Terms and Conditions from time to time without notice to
|
||||
you. You acknowledge and agree that it is your responsibility to review
|
||||
this Website and these Terms and Conditions from time to time and to
|
||||
familiarize yourself with any modifications. Your continued use of this
|
||||
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>
|
||||
These Terms and Conditions are effective as of{' '}
|
||||
<span className="date">06/22/2021</span>. We expressly reserve the right
|
||||
to change these Terms and Conditions from time to time without notice to
|
||||
you. You acknowledge and agree that it is your responsibility to review
|
||||
this Website and these Terms and Conditions from time to time and to
|
||||
familiarize yourself with any modifications. Your continued use of this
|
||||
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>
|
||||
|
||||
<h2>Conduct on Website</h2>
|
||||
<h2>Conduct on Website</h2>
|
||||
|
||||
<p>
|
||||
Your use of the Website is subject to all applicable laws and
|
||||
regulations, and you are solely responsible for the substance of your
|
||||
communications through the Website.
|
||||
</p>
|
||||
<p>
|
||||
Your use of the Website is subject to all applicable laws and regulations,
|
||||
and you are solely responsible for the substance of your communications
|
||||
through the Website.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
By posting information in or otherwise using any communications service,
|
||||
chat room, message board, newsgroup, software library, or other
|
||||
interactive service that may be available to you on or through this
|
||||
Website, you agree that you will not upload, share, post, or otherwise
|
||||
distribute or facilitate distribution of any content — including text,
|
||||
communications, software, images, sounds, data, or other information —
|
||||
that:
|
||||
</p>
|
||||
<p>
|
||||
By posting information in or otherwise using any communications service,
|
||||
chat room, message board, newsgroup, software library, or other
|
||||
interactive service that may be available to you on or through this
|
||||
Website, you agree that you will not upload, share, post, or otherwise
|
||||
distribute or facilitate distribution of any content — including text,
|
||||
communications, software, images, sounds, data, or other information —
|
||||
that:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Is unlawful, threatening, abusive, harassing, defamatory, libelous,
|
||||
deceptive, fraudulent, invasive of another's privacy, tortious,
|
||||
contains explicit or graphic descriptions or accounts of sexual acts
|
||||
(including 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
|
||||
</li>
|
||||
<li>
|
||||
Victimizes, harasses, degrades, or intimidates an individual or group
|
||||
of individuals on the basis of religion, gender, sexual orientation,
|
||||
race, ethnicity, age, or disability
|
||||
</li>
|
||||
<li>
|
||||
Infringes on any patent, trademark, trade secret, copyright, right of
|
||||
publicity, or other proprietary right of any party
|
||||
</li>
|
||||
<li>
|
||||
Constitutes unauthorized or unsolicited advertising, junk or bulk
|
||||
email (also known as "spamming"), chain letters, any other form of
|
||||
unauthorized solicitation, or any form of lottery or gambling
|
||||
</li>
|
||||
<li>
|
||||
Contains software viruses or any other computer code, files, or
|
||||
programs that are designed or intended to disrupt, damage, or limit
|
||||
the 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
|
||||
</li>
|
||||
<li>
|
||||
Impersonates any person or entity, including any of our employees or
|
||||
representatives
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
Is unlawful, threatening, abusive, harassing, defamatory, libelous,
|
||||
deceptive, fraudulent, invasive of another's privacy, tortious, contains
|
||||
explicit or graphic descriptions or accounts of sexual acts (including
|
||||
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
|
||||
</li>
|
||||
<li>
|
||||
Victimizes, harasses, degrades, or intimidates an individual or group of
|
||||
individuals on the basis of religion, gender, sexual orientation, race,
|
||||
ethnicity, age, or disability
|
||||
</li>
|
||||
<li>
|
||||
Infringes on any patent, trademark, trade secret, copyright, right of
|
||||
publicity, or other proprietary right of any party
|
||||
</li>
|
||||
<li>
|
||||
Constitutes unauthorized or unsolicited advertising, junk or bulk email
|
||||
(also known as "spamming"), chain letters, any other form of
|
||||
unauthorized solicitation, or any form of lottery or gambling
|
||||
</li>
|
||||
<li>
|
||||
Contains software viruses or any other computer code, files, or programs
|
||||
that are designed or intended to disrupt, damage, or limit the
|
||||
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
|
||||
</li>
|
||||
<li>
|
||||
Impersonates any person or entity, including any of our employees or
|
||||
representatives
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
We neither endorse nor assume any liability for the contents of any
|
||||
material uploaded or submitted by third party users of the Website. We
|
||||
generally do not pre-screen, monitor, or edit the content posted by
|
||||
users of communications services, chat rooms, message boards,
|
||||
newsgroups, software libraries, or other interactive services that may
|
||||
be available on or through this Website.
|
||||
</p>
|
||||
<p>
|
||||
We neither endorse nor assume any liability for the contents of any
|
||||
material uploaded or submitted by third party users of the Website. We
|
||||
generally do not pre-screen, monitor, or edit the content posted by users
|
||||
of communications services, chat rooms, message boards, newsgroups,
|
||||
software libraries, or other interactive services that may be available on
|
||||
or through this Website.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
However, we and our agents have the right at their sole discretion to
|
||||
remove any content that, in our judgment, does not comply with these
|
||||
Terms of Use and any other rules of user conduct for our Website, or is
|
||||
otherwise harmful, objectionable, or inaccurate. We are not responsible
|
||||
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>
|
||||
However, we and our agents have the right at their sole discretion to
|
||||
remove any content that, in our judgment, does not comply with these Terms
|
||||
of Use and any other rules of user conduct for our Website, or is
|
||||
otherwise harmful, objectionable, or inaccurate. We are not responsible
|
||||
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>
|
||||
You agree that we may at any time, and at our sole discretion, terminate
|
||||
your membership, account, or other affiliation with our site without
|
||||
prior notice to you for violating any of the above provisions.
|
||||
</p>
|
||||
<p>
|
||||
You agree that we may at any time, and at our sole discretion, terminate
|
||||
your membership, account, or other affiliation with our site without prior
|
||||
notice to you for violating any of the above provisions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In addition, you acknowledge that we will cooperate fully with
|
||||
investigations of violations of systems or network security at other
|
||||
sites, including cooperating with law enforcement authorities in
|
||||
investigating suspected criminal violations.
|
||||
</p>
|
||||
<p>
|
||||
In addition, you acknowledge that we will cooperate fully with
|
||||
investigations of violations of systems or network security at other
|
||||
sites, including cooperating with law enforcement authorities in
|
||||
investigating suspected criminal violations.
|
||||
</p>
|
||||
|
||||
<h2>Intellectual Property</h2>
|
||||
<h2>Intellectual Property</h2>
|
||||
|
||||
<p>
|
||||
By accepting these Terms and Conditions, you acknowledge and agree that
|
||||
all content presented to you on this Website is protected by copyrights,
|
||||
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>.
|
||||
</p>
|
||||
<p>
|
||||
By accepting these Terms and Conditions, you acknowledge and agree that
|
||||
all content presented to you on this Website is protected by copyrights,
|
||||
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>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You are only permitted to use the content as expressly authorized by us
|
||||
or the specific content provider. Except for a single copy made for
|
||||
personal use only, you may not copy, reproduce, modify, republish,
|
||||
upload, post, transmit, or distribute any documents or information from
|
||||
this Website in any form or by any means without prior written
|
||||
permission from us or the specific content provider, and you are solely
|
||||
responsible for obtaining permission before reusing any copyrighted
|
||||
material that is available on this Website.
|
||||
</p>
|
||||
<p>
|
||||
You are only permitted to use the content as expressly authorized by us or
|
||||
the specific content provider. Except for a single copy made for personal
|
||||
use only, you may not copy, reproduce, modify, republish, upload, post,
|
||||
transmit, or distribute any documents or information from this Website in
|
||||
any form or by any means without prior written permission from us or the
|
||||
specific content provider, and you are solely responsible for obtaining
|
||||
permission before reusing any copyrighted material that is available on
|
||||
this Website.
|
||||
</p>
|
||||
|
||||
<h2>Third Party Websites</h2>
|
||||
<h2>Third Party Websites</h2>
|
||||
|
||||
<p>
|
||||
This Website may link you to other sites on the Internet or otherwise
|
||||
include references to information, documents, software, materials and/or
|
||||
services provided by other parties. These websites may contain
|
||||
information or material that some people may find inappropriate or
|
||||
offensive.
|
||||
</p>
|
||||
<p>
|
||||
This Website may link you to other sites on the Internet or otherwise
|
||||
include references to information, documents, software, materials and/or
|
||||
services provided by other parties. These websites may contain information
|
||||
or material that some people may find inappropriate or offensive.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
These other websites and parties are not under our control, and you
|
||||
acknowledge that we are not responsible for the accuracy, copyright
|
||||
compliance, legality, decency, or any other aspect of the content of
|
||||
such sites, nor are we responsible for errors or omissions in any
|
||||
references to other parties or their products and services. The
|
||||
inclusion of such a link 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>
|
||||
These other websites and parties are not under our control, and you
|
||||
acknowledge that we are not responsible for the accuracy, copyright
|
||||
compliance, legality, decency, or any other aspect of the content of such
|
||||
sites, nor are we responsible for errors or omissions in any references to
|
||||
other parties or their products and services. The inclusion of such a link
|
||||
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>
|
||||
|
||||
<h2>
|
||||
Disclaimer of Warranties, Limitations of Liability and Indemnification
|
||||
</h2>
|
||||
<h2>
|
||||
Disclaimer of Warranties, Limitations of Liability and Indemnification
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
Your use of{" "}
|
||||
<span className='website_name'>{process.env.NEXT_PUBLIC_TITLE}</span> is
|
||||
at your sole risk. The Website is provided "as is" and "as available".
|
||||
We 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>
|
||||
Your use of{' '}
|
||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> is
|
||||
at your sole risk. The Website is provided "as is" and "as available". We
|
||||
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>
|
||||
We are not liable for damages, direct or consequential, resulting from
|
||||
your use of the Website, and you agree to defend, indemnify and hold us
|
||||
harmless from any claims, losses, liability costs and expenses
|
||||
(including but not limites to attorney's fees) arising from your
|
||||
violation of any third-party's rights. You acknowledge that you have
|
||||
only a limited, non-exclusive, nontransferable license to use the
|
||||
Website. Because the 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>
|
||||
We are not liable for damages, direct or consequential, resulting from
|
||||
your use of the Website, and you agree to defend, indemnify and hold us
|
||||
harmless from any claims, losses, liability costs and expenses (including
|
||||
but not limites to attorney's fees) arising from your violation of any
|
||||
third-party's rights. You acknowledge that you have only a limited,
|
||||
non-exclusive, nontransferable license to use the Website. Because the
|
||||
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>
|
||||
|
||||
<h2>Term and termination</h2>
|
||||
<h2>Term and termination</h2>
|
||||
|
||||
<p>
|
||||
This Terms and Conditions will become effective in relation to you when
|
||||
you create a{" "}
|
||||
<span className='website_name'>{process.env.NEXT_PUBLIC_TITLE}</span>{" "}
|
||||
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>
|
||||
This Terms and Conditions will become effective in relation to you when
|
||||
you create a{' '}
|
||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||
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>
|
||||
<span className='website_name'>{process.env.NEXT_PUBLIC_TITLE}</span>{" "}
|
||||
reserves the right to terminate this Terms and Conditions or suspend
|
||||
your account at any time in case of unauthorized, or suspected
|
||||
unauthorized use of the Website whether in contravention of this Terms
|
||||
and Conditions or otherwise. If{" "}
|
||||
<span className='website_name'>{process.env.NEXT_PUBLIC_TITLE}</span>{" "}
|
||||
terminates this Terms 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>{" "}
|
||||
shall have no liability or responsibility to you.
|
||||
</p>
|
||||
<p>
|
||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||
reserves the right to terminate this Terms and Conditions or suspend your
|
||||
account at any time in case of unauthorized, or suspected unauthorized use
|
||||
of the Website whether in contravention of this Terms and Conditions or
|
||||
otherwise. If{' '}
|
||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span>{' '}
|
||||
terminates this Terms 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>{' '}
|
||||
shall have no liability or responsibility to you.
|
||||
</p>
|
||||
|
||||
<h2>Assignment</h2>
|
||||
<h2>Assignment</h2>
|
||||
|
||||
<p>
|
||||
<span className='website_name'>{process.env.NEXT_PUBLIC_TITLE}</span>{" "}
|
||||
may assign this Terms and Conditions or any part of it without
|
||||
restrictions. You may not assign this Terms and Conditions or any part
|
||||
of it to any third party.
|
||||
</p>
|
||||
<p>
|
||||
<span className="website_name">{process.env.NEXT_PUBLIC_TITLE}</span> may
|
||||
assign this Terms and Conditions or any part of it without restrictions.
|
||||
You may not assign this Terms and Conditions or any part of it to any
|
||||
third party.
|
||||
</p>
|
||||
|
||||
<h2>Governing Law</h2>
|
||||
<h2>Governing Law</h2>
|
||||
|
||||
<p>
|
||||
These Terms and Conditions and any dispute or claim arising out of, or
|
||||
related to them, shall be governed by and construed in accordance with
|
||||
the internal laws of the <span className='country'>fr</span> without
|
||||
giving effect to any choice or conflict of law provision or rule.
|
||||
</p>
|
||||
<p>
|
||||
These Terms and Conditions and any dispute or claim arising out of, or
|
||||
related to them, shall be governed by and construed in accordance with the
|
||||
internal laws of the <span className="country">fr</span> without giving
|
||||
effect to any choice or conflict of law provision or rule.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Any legal suit, action or proceeding arising out of, or related to,
|
||||
these Terms of Service or the Website shall be instituted exclusively in
|
||||
the federal courts of <span className='country'>fr</span>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<p>
|
||||
Any legal suit, action or proceeding arising out of, or related to, these
|
||||
Terms of Service or the Website shall be instituted exclusively in the
|
||||
federal courts of <span className="country">fr</span>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Terms;
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
/*
|
||||
/*
|
||||
This card is used on the landing page
|
||||
*/
|
||||
|
||||
import Image from "next/image";
|
||||
import Image from 'next/image';
|
||||
|
||||
const CardLanding = (props) => {
|
||||
const { image, title, text } = props;
|
||||
return (
|
||||
<div className='w-80 h-48 p-5 sm:ml-5 mb-5 bg-base-100 flex'>
|
||||
<div className="w-80 h-48 p-5 sm:ml-5 mb-5 bg-base-100 flex">
|
||||
<div>
|
||||
<div className='rounded-full w-12 h-12 border flex bg-neutral-content'>
|
||||
<div className='m-auto flex'>
|
||||
<Image src={props.image} width={24} height={24} />
|
||||
<div className="rounded-full w-12 h-12 border flex bg-neutral-content">
|
||||
<div className="m-auto flex">
|
||||
<Image src={image} width={24} height={24} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ml-8'>
|
||||
<p className='font-semibold font-title text-lg'>{props.title}</p>
|
||||
<p className='mt-3'>{props.text}</p>
|
||||
<div className="ml-8">
|
||||
<p className="font-semibold font-title text-lg">{title}</p>
|
||||
<p className="mt-3">{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
This card is used on the landing page
|
||||
*/
|
||||
|
||||
import { FiStar } from "react-icons/fi";
|
||||
import { FiStar } from 'react-icons/fi';
|
||||
|
||||
const KeyFeature = (props) => {
|
||||
return (
|
||||
<div className='shadow-sm p-5 mb-5 bg-base-100 flex italic'>
|
||||
<div className='p-2 bg-accent-focus w-12 h-12 text-white rounded-sm my-auto flex'>
|
||||
<FiStar className="text-2xl m-auto"/>
|
||||
</div>
|
||||
<div className='m-auto ml-3'>{props.children}</div>
|
||||
const KeyFeature = (props) => (
|
||||
<div className="shadow-sm p-5 mb-5 bg-base-100 flex italic">
|
||||
<div className="p-2 bg-accent-focus w-12 h-12 text-white rounded-sm my-auto flex">
|
||||
<FiStar className="text-2xl m-auto" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<div className="m-auto ml-3">{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default KeyFeature;
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { IoLogoGoogle } from "react-icons/io";
|
||||
import router from "next/router";
|
||||
import { toast } from "react-toastify";
|
||||
import { useState } from "react";
|
||||
import { IoLogoGoogle } from 'react-icons/io';
|
||||
import router from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useState } from 'react';
|
||||
|
||||
const Login = (props) => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [forgot, setForgot] = useState(false);
|
||||
|
||||
const resetPassword = () => {
|
||||
props.resetPassword(email).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(result.error.message);
|
||||
} else toast.success("Check your email to reset your password!");
|
||||
} else toast.success('Check your email to reset your password!');
|
||||
});
|
||||
};
|
||||
|
||||
const login = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
//Handle the login. Go to the homepage if success or display an error.
|
||||
// Handle the login. Go to the homepage if success or display an error.
|
||||
props
|
||||
.signIn({
|
||||
email: email,
|
||||
password: password,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.data) {
|
||||
router.push("/");
|
||||
router.push('/');
|
||||
}
|
||||
if (result.error) {
|
||||
toast.error(result.error.message);
|
||||
@@ -36,45 +36,46 @@ const Login = (props) => {
|
||||
};
|
||||
|
||||
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="p-10 bg-base-100 md:flex-1 rounded-md text-base-content shadow-md max-w-sm font-body">
|
||||
{!forgot && (
|
||||
<>
|
||||
<h3 className='my-4 text-2xl font-semibold font-title'>
|
||||
<h3 className="my-4 text-2xl font-semibold font-title">
|
||||
Account Login
|
||||
</h3>
|
||||
<form action='#' className='flex flex-col space-y-5'>
|
||||
<div className='flex flex-col space-y-1'>
|
||||
<label htmlFor='email' className='text-sm'>
|
||||
<form action="#" className="flex flex-col space-y-5">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<label htmlFor="email" className="text-sm">
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
type='email'
|
||||
id='email'
|
||||
type="email"
|
||||
id="email"
|
||||
autoFocus
|
||||
className='input input-primary input-bordered input-sm'
|
||||
className="input input-primary input-bordered input-sm"
|
||||
value={email}
|
||||
onChange={(event) => {
|
||||
setEmail(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col space-y-1'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<label htmlFor='password' className='text-sm'>
|
||||
<div className="flex flex-col space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="password" className="text-sm">
|
||||
Password
|
||||
</label>
|
||||
<button
|
||||
onClick={() => {
|
||||
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?
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type='password'
|
||||
id='password'
|
||||
className='input input-primary input-bordered input-sm'
|
||||
type="password"
|
||||
id="password"
|
||||
className="input input-primary input-bordered input-sm"
|
||||
value={password}
|
||||
onChange={(event) => {
|
||||
setPassword(event.target.value);
|
||||
@@ -84,31 +85,33 @@ const Login = (props) => {
|
||||
|
||||
<div>
|
||||
<button
|
||||
className='btn btn-primary w-full'
|
||||
className="btn btn-primary w-full"
|
||||
onClick={(event) => {
|
||||
login(event);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Log in
|
||||
</button>
|
||||
</div>
|
||||
<div className='flex flex-col space-y-5'>
|
||||
<span className='flex items-center justify-center space-x-2'>
|
||||
<span className='h-px bg-gray-400 w-14'></span>
|
||||
<span className='font-normal text-gray-500'>or login with</span>
|
||||
<span className='h-px bg-gray-400 w-14'></span>
|
||||
<div className="flex flex-col space-y-5">
|
||||
<span className="flex items-center justify-center space-x-2">
|
||||
<span className="h-px bg-gray-400 w-14" />
|
||||
<span className="font-normal text-gray-500">or login with</span>
|
||||
<span className="h-px bg-gray-400 w-14" />
|
||||
</span>
|
||||
<div className='flex flex-col space-y-4'>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<button
|
||||
href='#'
|
||||
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 '
|
||||
href="#"
|
||||
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 "
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
props.signIn({ provider: "google" });
|
||||
}}>
|
||||
<div className='text-base-content'>
|
||||
props.signIn({ provider: 'google' });
|
||||
}}
|
||||
>
|
||||
<div className="text-base-content">
|
||||
<IoLogoGoogle />
|
||||
</div>
|
||||
<span className='text-sm font-medium text-base-content'>
|
||||
<span className="text-sm font-medium text-base-content">
|
||||
Gmail
|
||||
</span>
|
||||
</button>
|
||||
@@ -119,19 +122,20 @@ const Login = (props) => {
|
||||
)}
|
||||
{forgot && (
|
||||
<>
|
||||
<h3 className='my-4 text-2xl font-semibold'>Password recovery</h3>
|
||||
<form action='#' className='flex flex-col space-y-5'>
|
||||
<div className='flex flex-col space-y-1'>
|
||||
<h3 className="my-4 text-2xl font-semibold">Password recovery</h3>
|
||||
<form action="#" className="flex flex-col space-y-5">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<label
|
||||
htmlFor='email'
|
||||
className='text-sm font-semibold text-gray-500'>
|
||||
htmlFor="email"
|
||||
className="text-sm font-semibold text-gray-500"
|
||||
>
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
type='email'
|
||||
id='email'
|
||||
type="email"
|
||||
id="email"
|
||||
autoFocus
|
||||
className='input input-primary input-bordered input-sm'
|
||||
className="input input-primary input-bordered input-sm"
|
||||
value={email}
|
||||
onChange={(event) => {
|
||||
setEmail(event.target.value);
|
||||
@@ -141,11 +145,12 @@ const Login = (props) => {
|
||||
|
||||
<div>
|
||||
<button
|
||||
className='btn btn-primary w-full btn-sm'
|
||||
className="btn btn-primary w-full btn-sm"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
resetPassword();
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Recover my password
|
||||
</button>
|
||||
</div>
|
||||
@@ -154,7 +159,8 @@ const Login = (props) => {
|
||||
onClick={() => {
|
||||
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
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { IoLogoGoogle } from "react-icons/io";
|
||||
import router from "next/router";
|
||||
import { toast } from "react-toastify";
|
||||
import { useState } from "react";
|
||||
import { IoLogoGoogle } from 'react-icons/io';
|
||||
import router from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useState } from 'react';
|
||||
|
||||
const SignUpPanel = (props) => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [forgot, setForgot] = useState(false);
|
||||
|
||||
const resetPassword = () => {
|
||||
props.resetPassword(email).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(result.error.message);
|
||||
} else toast.success("Check your email to reset your password!");
|
||||
} else toast.success('Check your email to reset your password!');
|
||||
});
|
||||
};
|
||||
|
||||
const signup = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
//Handle the login. Go to the homepage if success or display an error.
|
||||
// Handle the login. Go to the homepage if success or display an error.
|
||||
props
|
||||
.signUp({
|
||||
email: email,
|
||||
password: password,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.data) {
|
||||
router.push("/");
|
||||
router.push('/');
|
||||
}
|
||||
if (result.error) {
|
||||
toast.error(result.error.message);
|
||||
@@ -36,33 +36,33 @@ const SignUpPanel = (props) => {
|
||||
};
|
||||
|
||||
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="p-10 bg-base-100 md:flex-1 rounded-md text-base-content shadow-md max-w-sm font-body">
|
||||
{!forgot && (
|
||||
<>
|
||||
<h3 className='my-4 text-2xl font-semibold font-title'>
|
||||
<h3 className="my-4 text-2xl font-semibold font-title">
|
||||
Account Sign Up
|
||||
</h3>
|
||||
<form action='#' className='flex flex-col space-y-5'>
|
||||
<div className='flex flex-col space-y-1'>
|
||||
<label htmlFor='email' className='text-sm'>
|
||||
<form action="#" className="flex flex-col space-y-5">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<label htmlFor="email" className="text-sm">
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
type='email'
|
||||
id='email'
|
||||
type="email"
|
||||
id="email"
|
||||
autoFocus
|
||||
className='input input-primary input-bordered input-sm'
|
||||
className="input input-primary input-bordered input-sm"
|
||||
value={email}
|
||||
onChange={(event) => {
|
||||
setEmail(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col space-y-1'>
|
||||
<div className="flex flex-col space-y-1">
|
||||
<input
|
||||
type='password'
|
||||
id='password'
|
||||
className='input input-primary input-bordered input-sm'
|
||||
type="password"
|
||||
id="password"
|
||||
className="input input-primary input-bordered input-sm"
|
||||
value={password}
|
||||
onChange={(event) => {
|
||||
setPassword(event.target.value);
|
||||
@@ -72,33 +72,35 @@ const SignUpPanel = (props) => {
|
||||
|
||||
<div>
|
||||
<button
|
||||
className='btn btn-primary w-full'
|
||||
className="btn btn-primary w-full"
|
||||
onClick={(event) => {
|
||||
signup(event);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
<div className='flex flex-col space-y-5'>
|
||||
<span className='flex items-center justify-center space-x-2'>
|
||||
<span className='h-px bg-gray-400 w-14'></span>
|
||||
<span className='font-normal text-gray-500'>
|
||||
<div className="flex flex-col space-y-5">
|
||||
<span className="flex items-center justify-center space-x-2">
|
||||
<span className="h-px bg-gray-400 w-14" />
|
||||
<span className="font-normal text-gray-500">
|
||||
or sign up with
|
||||
</span>
|
||||
<span className='h-px bg-gray-400 w-14'></span>
|
||||
<span className="h-px bg-gray-400 w-14" />
|
||||
</span>
|
||||
<div className='flex flex-col space-y-4'>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<button
|
||||
href='#'
|
||||
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 '
|
||||
href="#"
|
||||
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 "
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
props.signIn({ provider: "google" });
|
||||
}}>
|
||||
<div className='text-base-content'>
|
||||
props.signIn({ provider: 'google' });
|
||||
}}
|
||||
>
|
||||
<div className="text-base-content">
|
||||
<IoLogoGoogle />
|
||||
</div>
|
||||
<span className='text-sm font-medium text-base-content'>
|
||||
<span className="text-sm font-medium text-base-content">
|
||||
Gmail
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -3,29 +3,29 @@ This component will handle the theme (dark/light). You are able to change the se
|
||||
DaisyUI have more than 10 themes availables https://daisyui.com/docs/default-themes
|
||||
*/
|
||||
|
||||
import { HiOutlineMoon, HiOutlineSun } from "react-icons/hi";
|
||||
import { useEffect, useState } from "react";
|
||||
import { HiOutlineMoon, HiOutlineSun } from 'react-icons/hi';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const theme = {
|
||||
primary: "supaTheme",
|
||||
secondary: "dark",
|
||||
primary: 'supaTheme',
|
||||
secondary: 'dark',
|
||||
};
|
||||
|
||||
const ThemeToggle = () => {
|
||||
const [activeTheme, setActiveTheme] = useState(document.body.dataset.theme);
|
||||
const inactiveTheme = activeTheme === "supaTheme" ? "dark" : "supaTheme";
|
||||
const inactiveTheme = activeTheme === 'supaTheme' ? 'dark' : 'supaTheme';
|
||||
|
||||
useEffect(() => {
|
||||
document.body.dataset.theme = activeTheme;
|
||||
window.localStorage.setItem("theme", activeTheme);
|
||||
window.localStorage.setItem('theme', activeTheme);
|
||||
}, [activeTheme]);
|
||||
|
||||
return (
|
||||
<button className='flex ml-3' onClick={() => setActiveTheme(inactiveTheme)}>
|
||||
<button className="flex ml-3" onClick={() => setActiveTheme(inactiveTheme)}>
|
||||
{activeTheme === theme.secondary ? (
|
||||
<HiOutlineSun className='m-auto text-xl hover:text-accent' />
|
||||
<HiOutlineSun className="m-auto text-xl hover:text-accent" />
|
||||
) : (
|
||||
<HiOutlineMoon className='m-auto text-xl hover:text-accent' />
|
||||
<HiOutlineMoon className="m-auto text-xl hover:text-accent" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user