mirror of
https://github.com/fergalmoran/supanextail.git
synced 2025-12-22 09:17:54 +00:00
Merge pull request #177 from Etheonor/feature/clean
Eslint massive clean
This commit is contained in:
@@ -8,20 +8,30 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"next",
|
"next",
|
||||||
"next/core-web-vitals",
|
"next/core-web-vitals",
|
||||||
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"plugin:sonarjs/recommended",
|
||||||
|
"plugin:unicorn/recommended"
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaFeatures": {
|
"project": [
|
||||||
"jsx": true
|
"./tsconfig.json"
|
||||||
},
|
]
|
||||||
"ecmaVersion": 12,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
},
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"ignorePatterns": [
|
||||||
|
"node_modules/*",
|
||||||
|
".next/*",
|
||||||
|
".out/*",
|
||||||
|
"!.prettierrc.js",
|
||||||
|
"types/*",
|
||||||
|
"next-env.d.ts"
|
||||||
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@typescript-eslint",
|
"@typescript-eslint",
|
||||||
"cypress",
|
"cypress",
|
||||||
"simple-import-sort",
|
"simple-import-sort",
|
||||||
"prettier"
|
"prettier",
|
||||||
|
"sonarjs"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
@@ -61,6 +71,16 @@
|
|||||||
"allowHigherOrderFunctions": true,
|
"allowHigherOrderFunctions": true,
|
||||||
"allowConciseArrowFunctionExpressionsStartingWithVoid": true
|
"allowConciseArrowFunctionExpressionsStartingWithVoid": true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"unicorn/no-null": "off",
|
||||||
|
"unicorn/filename-case": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"cases": {
|
||||||
|
"camelCase": true,
|
||||||
|
"pascalCase": true
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.next
|
.next
|
||||||
dist
|
dist
|
||||||
node_modules/
|
node_modules/
|
||||||
|
types/
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
## Welcome to SupaNexTail!
|
## Welcome to SupaNexTail!
|
||||||
|
|
||||||
## Documentation 2.0
|
## Documentation 2.0
|
||||||
A new documentation is available here : https://doc.supanextail.dev/
|
|
||||||
|
|
||||||
|
A new documentation is available here : https://doc.supanextail.dev/
|
||||||
|
|
||||||
## 
|
## 
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Simply follow the installation process. You need to have some knowledge with Rea
|
|||||||
|
|
||||||
#### SupaNexTail project
|
#### SupaNexTail project
|
||||||
|
|
||||||
You'll need to fork this repository. I suggest to keep your repository sync with SupaNexTail, in order to get all future updates.
|
You'll need to fork this repository. I suggest to keep your repository sync with SupaNexTail, in order to get all future updates.
|
||||||
|
|
||||||
To do that, you'll have extended information on this page: https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks/syncing-a-fork
|
To do that, you'll have extended information on this page: https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks/syncing-a-fork
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ Notes:
|
|||||||
If you want to use Stripe, be sure to set up your webhooks in the dashboard. If you want to test it locally, install Stripe CLI and use this command line:
|
If you want to use Stripe, be sure to set up your webhooks in the dashboard. If you want to test it locally, install Stripe CLI and use this command line:
|
||||||
|
|
||||||
```
|
```
|
||||||
stripe listen --forward-to localhost:3000/api/stripe/stripe-webhook
|
stripe listen --forward-to localhost:3000/api/stripe/webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
The two event needed are:
|
The two event needed are:
|
||||||
|
|||||||
@@ -9,25 +9,25 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { supabase } from 'utils/supabaseClient';
|
import { supabase } from 'utils/supabaseClient';
|
||||||
|
|
||||||
type AvatarProps = {
|
type AvatarProperties = {
|
||||||
url: string;
|
url: string;
|
||||||
size: number;
|
size: number;
|
||||||
onUpload: (filePath: string) => void;
|
onUpload: (filePath: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
const customImgLoader = ({ src }: { src: string }): string => {
|
||||||
|
return `${src}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Avatar = ({ url, size, onUpload }: AvatarProperties): JSX.Element => {
|
||||||
const [avatarUrl, setAvatarUrl] = useState('');
|
const [avatarUrl, setAvatarUrl] = useState('');
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
|
|
||||||
const customImgLoader = ({ src }: { src: string }) => {
|
|
||||||
return `${src}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (url) downloadImage(url);
|
if (url) void downloadImage(url);
|
||||||
}, [url]);
|
}, [url]);
|
||||||
|
|
||||||
async function downloadImage(path: string) {
|
async function downloadImage(path: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase.storage
|
const { data, error } = await supabase.storage
|
||||||
.from('avatars')
|
.from('avatars')
|
||||||
@@ -41,12 +41,14 @@ const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
|||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
console.log('Error downloading image: ', error.message);
|
console.log('Error downloading image:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadAvatar(event: React.ChangeEvent<HTMLInputElement>) {
|
async function uploadAvatar(
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
|
|
||||||
@@ -55,11 +57,11 @@ const Avatar = ({ url, size, onUpload }: AvatarProps): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
const fileExt = file.name.split('.').pop();
|
const fileExtension = file.name.split('.').pop();
|
||||||
const fileName = `${Math.random()}.${fileExt}`;
|
const fileName = fileExtension ? `${Math.random()}.${fileExtension}` : '';
|
||||||
const filePath = `${fileName}`;
|
const filePath = `${fileName}`;
|
||||||
|
|
||||||
if (event.target.files[0].size > 150000) {
|
if (event.target.files[0].size > 150_000) {
|
||||||
alert('File is too big!');
|
alert('File is too big!');
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
setUploading(false);
|
setUploading(false);
|
||||||
|
|||||||
@@ -19,37 +19,37 @@ const CardsLanding = (): JSX.Element => (
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap justify-center mt-10">
|
<div className="flex flex-wrap justify-center mt-10">
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardPage}
|
image={cardPage as string}
|
||||||
text="7 pages fully designed and easily customizable"
|
text="7 pages fully designed and easily customizable"
|
||||||
title="Templates"
|
title="Templates"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardServer}
|
image={cardServer as string}
|
||||||
text="Integrated backend already setup with Next.js API Routes"
|
text="Integrated backend already setup with Next.js API Routes"
|
||||||
title="Backend"
|
title="Backend"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardAuth}
|
image={cardAuth as string}
|
||||||
text="Auth and user management with Supabase"
|
text="Auth and user management with Supabase"
|
||||||
title="Auth"
|
title="Auth"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardResponsive}
|
image={cardResponsive as string}
|
||||||
text="Mobile ready, fully responsive and customizable with Tailwind CSS"
|
text="Mobile ready, fully responsive and customizable with Tailwind CSS"
|
||||||
title="Responsive"
|
title="Responsive"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardTheme}
|
image={cardTheme as string}
|
||||||
text="Custom themes available and easily switch to dark mode"
|
text="Custom themes available and easily switch to dark mode"
|
||||||
title="Themes"
|
title="Themes"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardStripe}
|
image={cardStripe as string}
|
||||||
text="Stripe integration. Fully functional subscription system"
|
text="Stripe integration. Fully functional subscription system"
|
||||||
title="Payment"
|
title="Payment"
|
||||||
/>
|
/>
|
||||||
<CardLanding
|
<CardLanding
|
||||||
image={cardFee}
|
image={cardFee as string}
|
||||||
text="One-time fee. No subscription, you’ll have access to all the updates"
|
text="One-time fee. No subscription, you’ll have access to all the updates"
|
||||||
title="Lifetime access"
|
title="Lifetime access"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,39 +9,47 @@ the axios.post here, line 18.
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
const Contact = (): JSX.Element => {
|
const sendEmail = (): void => {
|
||||||
const sendEmail = () => {
|
const name = (document.querySelector('#name') as HTMLInputElement).value;
|
||||||
const name = (document.getElementById('name') as HTMLInputElement).value;
|
const email = (document.querySelector('#email') as HTMLInputElement).value;
|
||||||
const email = (document.getElementById('email') as HTMLInputElement).value;
|
const message = (document.querySelector('#message') as HTMLInputElement)
|
||||||
const message = (document.getElementById('message') as HTMLInputElement)
|
.value;
|
||||||
.value;
|
|
||||||
|
|
||||||
if (name && email && message) {
|
if (name && email && message) {
|
||||||
axios
|
axios
|
||||||
.post('/api/sendgrid', { email, name, message })
|
.post('/api/sendgrid', { email, name, message })
|
||||||
.then((result) => {
|
.then(
|
||||||
|
(result: {
|
||||||
|
data: {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
if (result.data.success === true) {
|
if (result.data.success === true) {
|
||||||
toast.success(result.data.message);
|
toast.success(result.data.message);
|
||||||
(document.getElementById('name') as HTMLInputElement).value = '';
|
(document.querySelector('#name') as HTMLInputElement).value = '';
|
||||||
(document.getElementById('email') as HTMLInputElement).value = '';
|
(document.querySelector('#email') as HTMLInputElement).value = '';
|
||||||
(document.getElementById('message') as HTMLInputElement).value = '';
|
(document.querySelector('#message') as HTMLInputElement).value = '';
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.catch((err) => {
|
)
|
||||||
console.log(err);
|
.catch((error) => {
|
||||||
});
|
console.log(error);
|
||||||
} else {
|
|
||||||
toast.info('Please fill all the fields ', {
|
|
||||||
position: 'top-center',
|
|
||||||
autoClose: 2000,
|
|
||||||
hideProgressBar: true,
|
|
||||||
closeOnClick: true,
|
|
||||||
pauseOnHover: true,
|
|
||||||
draggable: true,
|
|
||||||
progress: undefined,
|
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
};
|
toast.info('Please fill all the fields ', {
|
||||||
|
position: 'top-center',
|
||||||
|
autoClose: 2000,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Contact = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="max-w-xl px-5 py-10 m-auto">
|
<div className="max-w-xl px-5 py-10 m-auto">
|
||||||
<div>
|
<div>
|
||||||
@@ -87,8 +95,8 @@ const Contact = (): JSX.Element => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary btn-sm"
|
className="btn btn-primary btn-sm"
|
||||||
onClick={(e) => {
|
onClick={(event) => {
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
sendEmail();
|
sendEmail();
|
||||||
}}>
|
}}>
|
||||||
Submit{' '}
|
Submit{' '}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { supabase } from '../utils/supabaseClient';
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
type DashboardProps = {
|
type DashboardProperties = {
|
||||||
profile: { username: string; website: string; avatar_url: string };
|
profile: { username: string; website: string; avatar_url: string };
|
||||||
session: Session;
|
session: Session;
|
||||||
planName: string;
|
planName: string;
|
||||||
@@ -26,7 +26,7 @@ const Dashboard = ({
|
|||||||
profile,
|
profile,
|
||||||
session,
|
session,
|
||||||
planName,
|
planName,
|
||||||
}: DashboardProps): JSX.Element => {
|
}: DashboardProperties): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [username, setUsername] = useState(profile?.username || '');
|
const [username, setUsername] = useState(profile?.username || '');
|
||||||
@@ -48,7 +48,7 @@ const Dashboard = ({
|
|||||||
username: string;
|
username: string;
|
||||||
website: string;
|
website: string;
|
||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
}) {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const user = supabase.auth.user();
|
const user = supabase.auth.user();
|
||||||
@@ -89,7 +89,7 @@ const Dashboard = ({
|
|||||||
size={150}
|
size={150}
|
||||||
onUpload={(url) => {
|
onUpload={(url) => {
|
||||||
setAvatarUrl(url);
|
setAvatarUrl(url);
|
||||||
updateProfile({ username, website, avatar_url: url });
|
void updateProfile({ username, website, avatar_url: url });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col mb-5">
|
<div className="flex flex-col mb-5">
|
||||||
@@ -113,7 +113,7 @@ const Dashboard = ({
|
|||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
value={username || ''}
|
value={username || ''}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(event) => setUsername(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col mb-5">
|
<div className="flex flex-col mb-5">
|
||||||
@@ -125,7 +125,7 @@ const Dashboard = ({
|
|||||||
id="website"
|
id="website"
|
||||||
type="website"
|
type="website"
|
||||||
value={website || ''}
|
value={website || ''}
|
||||||
onChange={(e) => setWebsite(e.target.value)}
|
onChange={(event) => setWebsite(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ const Dashboard = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row flex-wrap w-full max-w-xl p-5 m-auto my-5 border-2 shadow-lg bordered border-primary">
|
<div className="flex flex-row flex-wrap w-full max-w-xl p-5 m-auto my-5 border-2 shadow-lg bordered border-primary">
|
||||||
<Image src={Plan} alt="credit card" />
|
<Image src={Plan as string} alt="credit card" />
|
||||||
<div className="flex flex-col m-auto">
|
<div className="flex flex-col m-auto">
|
||||||
<h2>Your current plan</h2>
|
<h2>Your current plan</h2>
|
||||||
<p className="">{planName}</p>
|
<p className="">{planName}</p>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const Landing = (): JSX.Element => (
|
|||||||
</div>
|
</div>
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
<Image
|
<Image
|
||||||
src={landTop}
|
src={landTop as string}
|
||||||
height={417}
|
height={417}
|
||||||
width={583}
|
width={583}
|
||||||
alt="Construction of a website"
|
alt="Construction of a website"
|
||||||
@@ -40,7 +40,7 @@ const Landing = (): JSX.Element => (
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
<Image src={start} alt="screenshot of the website" />
|
<Image src={start as string} alt="screenshot of the website" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap justify-around max-w-6xl m-auto mt-24">
|
<div className="flex flex-wrap justify-around max-w-6xl m-auto mt-24">
|
||||||
@@ -60,7 +60,10 @@ const Landing = (): JSX.Element => (
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex order-2 max-w-md lg:order-1">
|
<div className="flex order-2 max-w-md lg:order-1">
|
||||||
<Image src={supabaseImage} alt="screenshot of the Supabase website" />
|
<Image
|
||||||
|
src={supabaseImage as string}
|
||||||
|
alt="screenshot of the Supabase website"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MailingList />
|
<MailingList />
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import Nav from './Nav';
|
|||||||
import { ToastContainer } from 'react-toastify';
|
import { ToastContainer } from 'react-toastify';
|
||||||
import { useAuth } from 'utils/AuthContext';
|
import { useAuth } from 'utils/AuthContext';
|
||||||
|
|
||||||
type LayoutProps = {
|
type LayoutProperties = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Layout = ({ children }: LayoutProps): JSX.Element => {
|
const Layout = ({ children }: LayoutProperties): JSX.Element => {
|
||||||
const { user, signOut } = useAuth();
|
const { user, signOut } = useAuth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ const MailingList = (): JSX.Element => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [valid, setValid] = useState(true);
|
const [valid, setValid] = useState(true);
|
||||||
|
|
||||||
const validateEmail = () => {
|
const validateEmail = (): void => {
|
||||||
// Regex patern for email validation
|
// Regex patern for email validation
|
||||||
const 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,}))$/;
|
/^(([^\s"(),.:;<>@[\\\]]+(\.[^\s"(),.:;<>@[\\\]]+)*)|(".+"))@((\[(?:\d{1,3}\.){3}\d{1,3}])|(([\dA-Za-z\-]+\.)+[A-Za-z]{2,}))$/;
|
||||||
|
|
||||||
if (regex.test(mail)) {
|
if (regex.test(mail)) {
|
||||||
// this is a valid email address
|
// this is a valid email address
|
||||||
@@ -30,7 +30,7 @@ const MailingList = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscribe = () => {
|
const subscribe = (): void => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
axios
|
axios
|
||||||
.put('api/mailingList', {
|
.put('api/mailingList', {
|
||||||
@@ -38,12 +38,14 @@ const MailingList = (): JSX.Element => {
|
|||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.status === 200) {
|
if (result.status === 200) {
|
||||||
toast.success(result.data.message);
|
toast.success(
|
||||||
|
'Your email has been succesfully added to the mailing list. Welcome 👋'
|
||||||
|
);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((error) => {
|
||||||
console.log(err);
|
console.log(error);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -52,7 +54,7 @@ const MailingList = (): JSX.Element => {
|
|||||||
<h2 className="text-3xl font-bold text-center uppercase md:text-4xl font-title">
|
<h2 className="text-3xl font-bold text-center uppercase md:text-4xl font-title">
|
||||||
Stay Tuned
|
Stay Tuned
|
||||||
</h2>
|
</h2>
|
||||||
<Image src={Mailing} alt="Mail" />
|
<Image src={Mailing as string} alt="Mail" />
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<p className="max-w-md m-auto text-center">
|
<p className="max-w-md m-auto text-center">
|
||||||
Want to be the first to know when SupaNexTail launches and get an
|
Want to be the first to know when SupaNexTail launches and get an
|
||||||
@@ -61,13 +63,13 @@ const MailingList = (): JSX.Element => {
|
|||||||
</label>
|
</label>
|
||||||
<div className="m-auto mt-5">
|
<div className="m-auto mt-5">
|
||||||
<input
|
<input
|
||||||
onChange={(e) => {
|
onChange={(event) => {
|
||||||
setMail(e.target.value);
|
setMail(event.target.value);
|
||||||
}}
|
}}
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Your email"
|
placeholder="Your email"
|
||||||
className={`input input-primary input-bordered ${
|
className={`input input-primary input-bordered ${
|
||||||
valid ? null : 'input-error'
|
valid ? '' : 'input-error'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import { LogOut, Menu } from 'react-feather';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Logo from 'public/logo.svg';
|
import Logo from 'public/logo.svg';
|
||||||
|
import { User } from '@supabase/gotrue-js';
|
||||||
|
|
||||||
type NavProps = {
|
type NavProperties = {
|
||||||
user: Record<string, unknown>;
|
user: User | null | undefined;
|
||||||
signOut: () => void;
|
signOut: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
const Nav = ({ user, signOut }: NavProperties): JSX.Element => {
|
||||||
// Modify you menu directly here
|
// Modify you menu directly here
|
||||||
const NavMenu = (
|
const NavMenu = (
|
||||||
<>
|
<>
|
||||||
@@ -66,7 +67,7 @@ const Nav = ({ user, signOut }: NavProps): JSX.Element => {
|
|||||||
<nav className="w-full mb-2 navbar">
|
<nav className="w-full mb-2 navbar">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a>
|
<a>
|
||||||
<Image src={Logo} alt="SupaNexTail Logo" />
|
<Image src={Logo as string} alt="SupaNexTail Logo" />
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ import { Dialog, Transition } from '@headlessui/react';
|
|||||||
|
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
|
|
||||||
type PaymentModalProps = {
|
type PaymentModalProperties = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setPayment: (arg0: boolean) => void;
|
setPayment: (argument0: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PaymentModal = ({ open, setPayment }: PaymentModalProps): JSX.Element => {
|
const PaymentModal = ({
|
||||||
function closeModal() {
|
open,
|
||||||
|
setPayment,
|
||||||
|
}: PaymentModalProperties): JSX.Element => {
|
||||||
|
function closeModal(): void {
|
||||||
setPayment(false);
|
setPayment(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,20 @@ import { getSub, supabase } from 'utils/supabaseClient';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { definitions } from 'types/database/index';
|
||||||
import router from 'next/router';
|
import router from 'next/router';
|
||||||
import { useAuth } from 'utils/AuthContext';
|
import { useAuth } from 'utils/AuthContext';
|
||||||
|
|
||||||
const Pricing = (): JSX.Element => {
|
const Pricing = (): JSX.Element => {
|
||||||
const { user, session } = useAuth();
|
const { user, session } = useAuth();
|
||||||
const [customerId, setCustomerId] = useState<null | string>(null);
|
const [customerId, setCustomerId] = useState<undefined | string>();
|
||||||
const [sub, setSub] = useState(false);
|
const [sub, setSub] = useState<definitions['subscriptions'] | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
getSub().then((result) => setSub(result));
|
void getSub().then((result) => setSub(result));
|
||||||
supabase
|
void supabase
|
||||||
.from('subscriptions')
|
.from<definitions['subscriptions']>('subscriptions')
|
||||||
.select(`customer_id`)
|
.select(`customer_id`)
|
||||||
.eq('id', user.id)
|
.eq('id', user.id)
|
||||||
.single()
|
.single()
|
||||||
@@ -32,31 +33,37 @@ const Pricing = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
interface Test {
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmit = async (
|
const handleSubmit = async (
|
||||||
e: React.SyntheticEvent<HTMLButtonElement>,
|
event: React.SyntheticEvent<HTMLButtonElement>,
|
||||||
priceId: string
|
priceId: string
|
||||||
) => {
|
): Promise<void> => {
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
// Create a Checkout Session. This will redirect the user to the Stripe website for the payment.
|
// Create a Checkout Session. This will redirect the user to the Stripe website for the payment.
|
||||||
if (sub) {
|
if (user && session) {
|
||||||
axios
|
if (sub) {
|
||||||
.post('/api/stripe/customer-portal', {
|
const stripeInfo = await axios.post<Test>(
|
||||||
customerId,
|
'/api/stripe/customerPortal',
|
||||||
})
|
{
|
||||||
.then((result) => {
|
customerId,
|
||||||
router.push(result.data.url);
|
}
|
||||||
});
|
);
|
||||||
} else
|
void router.push(stripeInfo.data.url);
|
||||||
axios
|
} else {
|
||||||
.post('/api/stripe/create-checkout-session', {
|
const stripeInfo = await axios.post<Test>('/api/stripe/checkout', {
|
||||||
priceId,
|
priceId,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
customerId,
|
customerId,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
tokenId: session.access_token,
|
tokenId: session.access_token,
|
||||||
pay_mode: 'subscription',
|
pay_mode: 'subscription',
|
||||||
})
|
});
|
||||||
.then((result) => router.push(result.data.url));
|
void router.push(stripeInfo.data.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -102,8 +109,8 @@ const Pricing = (): JSX.Element => {
|
|||||||
{user ? (
|
{user ? (
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={(e) => {
|
onClick={(event) => {
|
||||||
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
void handleSubmit(event, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
||||||
}}>
|
}}>
|
||||||
{sub ? 'Handle subscription' : 'Subscribe'}
|
{sub ? 'Handle subscription' : 'Subscribe'}
|
||||||
</button>
|
</button>
|
||||||
@@ -136,8 +143,8 @@ const Pricing = (): JSX.Element => {
|
|||||||
{user ? (
|
{user ? (
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={(e) => {
|
onClick={(event) => {
|
||||||
handleSubmit(e, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
void handleSubmit(event, 'price_1JtHhaDMjD0UnVmM5uCyyrWn');
|
||||||
}}>
|
}}>
|
||||||
{sub ? 'Handle subscription' : 'Subscribe'}
|
{sub ? 'Handle subscription' : 'Subscribe'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import { SupabaseClient } from '@supabase/supabase-js';
|
|||||||
import { supabase } from 'utils/supabaseClient';
|
import { supabase } from 'utils/supabaseClient';
|
||||||
import { useAuth } from 'utils/AuthContext';
|
import { useAuth } from 'utils/AuthContext';
|
||||||
|
|
||||||
type ContainerProps = {
|
type ContainerProperties = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
supabaseClient: SupabaseClient;
|
supabaseClient: SupabaseClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Container = ({ children }: ContainerProps): JSX.Element => {
|
const Container = ({ children }: ContainerProperties): JSX.Element => {
|
||||||
const { user, signOut } = useAuth();
|
const { user, signOut } = useAuth();
|
||||||
if (user)
|
if (user)
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ This card is used on the landing page
|
|||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
type CardLandingProps = {
|
type CardLandingProperties = {
|
||||||
image: string;
|
image: string;
|
||||||
title: string;
|
title: string;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardLanding = ({ image, title, text }: CardLandingProps): JSX.Element => {
|
const CardLanding = ({
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
}: CardLandingProperties): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-48 p-5 mb-5 w-80 sm:ml-5 bg-base-100">
|
<div className="flex h-48 p-5 mb-5 w-80 sm:ml-5 bg-base-100">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ This card is used on the landing page
|
|||||||
|
|
||||||
import { FiStar } from 'react-icons/fi';
|
import { FiStar } from 'react-icons/fi';
|
||||||
|
|
||||||
type KeyFeatureProps = {
|
type KeyFeatureProperties = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
};
|
};
|
||||||
|
|
||||||
const KeyFeature = ({ children }: KeyFeatureProps): JSX.Element => (
|
const KeyFeature = ({ children }: KeyFeatureProperties): JSX.Element => (
|
||||||
<div className="flex p-5 mb-5 italic shadow-sm bg-base-100">
|
<div className="flex p-5 mb-5 italic shadow-sm bg-base-100">
|
||||||
<div className="flex w-12 h-12 p-2 my-auto text-white rounded-sm bg-accent-focus">
|
<div className="flex w-12 h-12 p-2 my-auto text-white rounded-sm bg-accent-focus">
|
||||||
<FiStar className="m-auto text-2xl" />
|
<FiStar className="m-auto text-2xl" />
|
||||||
|
|||||||
@@ -1,49 +1,53 @@
|
|||||||
|
import { ApiError, Session, UserCredentials } from '@supabase/gotrue-js';
|
||||||
|
|
||||||
import { IoLogoGoogle } from 'react-icons/io';
|
import { IoLogoGoogle } from 'react-icons/io';
|
||||||
import router from 'next/router';
|
import router from 'next/router';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
type LoginProps = {
|
type LoginProperties = {
|
||||||
resetPassword: (email: string) => Promise<{ error: { message: string } }>;
|
resetPassword: (data: string) => Promise<{
|
||||||
signIn: ({}) => Promise<{
|
data: {} | null;
|
||||||
data: Record<string, unknown>;
|
error: ApiError | null;
|
||||||
error: { message: string };
|
}>;
|
||||||
|
|
||||||
|
signIn: (data: UserCredentials) => Promise<{
|
||||||
|
user: Session['user'] | null;
|
||||||
|
session: Session | null;
|
||||||
|
error: ApiError | null;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
const Login = ({ resetPassword, signIn }: LoginProperties): JSX.Element => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [forgot, setForgot] = useState(false);
|
const [forgot, setForgot] = useState(false);
|
||||||
|
|
||||||
const resetPasswordLogin = () => {
|
const resetPasswordLogin = async (): Promise<void> => {
|
||||||
resetPassword(email).then((result: { error: { message: string } }) => {
|
await resetPassword(email).then((result: { error: ApiError | null }) => {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
toast.error(result.error.message);
|
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: React.SyntheticEvent<HTMLButtonElement>) => {
|
const login = async (
|
||||||
e.preventDefault();
|
event: React.SyntheticEvent<HTMLButtonElement>
|
||||||
|
): Promise<void> => {
|
||||||
|
event.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.
|
||||||
signIn({
|
await signIn({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
}).then(
|
}).then((result) => {
|
||||||
(result: {
|
if (result) {
|
||||||
data: Record<string, unknown>;
|
void router.push('/');
|
||||||
error: { message: string };
|
|
||||||
}) => {
|
|
||||||
if (result.data) {
|
|
||||||
router.push('/');
|
|
||||||
}
|
|
||||||
if (result.error) {
|
|
||||||
toast.error(result.error.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
if (result.error) {
|
||||||
|
toast.error(result.error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -97,7 +101,7 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
<button
|
<button
|
||||||
className="w-full btn btn-primary"
|
className="w-full btn btn-primary"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
login(event);
|
void login(event);
|
||||||
}}>
|
}}>
|
||||||
Log in
|
Log in
|
||||||
</button>
|
</button>
|
||||||
@@ -113,7 +117,7 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
signIn({ provider: 'google' });
|
void signIn({ provider: 'google' });
|
||||||
}}>
|
}}>
|
||||||
<div className="text-base-content">
|
<div className="text-base-content">
|
||||||
<IoLogoGoogle />
|
<IoLogoGoogle />
|
||||||
@@ -154,7 +158,7 @@ const Login = ({ resetPassword, signIn }: LoginProps): JSX.Element => {
|
|||||||
className="w-full btn btn-primary btn-sm"
|
className="w-full btn btn-primary btn-sm"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
resetPasswordLogin();
|
void resetPasswordLogin();
|
||||||
}}>
|
}}>
|
||||||
Recover my password
|
Recover my password
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,41 +1,50 @@
|
|||||||
|
import { ApiError, Session, UserCredentials } from '@supabase/gotrue-js';
|
||||||
|
|
||||||
import { IoLogoGoogle } from 'react-icons/io';
|
import { IoLogoGoogle } from 'react-icons/io';
|
||||||
import router from 'next/router';
|
import router from 'next/router';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
type SignUpPanelProps = {
|
type SignUpPanelProperties = {
|
||||||
signIn: ({}) => Promise<{
|
signUp: (data: UserCredentials) => Promise<{
|
||||||
data: Record<string, unknown>;
|
user: Session['user'] | null;
|
||||||
error: { message: string };
|
session: Session | null;
|
||||||
|
error: ApiError | null;
|
||||||
}>;
|
}>;
|
||||||
signUp: ({}) => Promise<{
|
|
||||||
data: Record<string, unknown>;
|
signIn: (data: UserCredentials) => Promise<{
|
||||||
error: { message: string };
|
user: Session['user'] | null;
|
||||||
|
session: Session | null;
|
||||||
|
error: ApiError | null;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
const SignUpPanel = ({
|
||||||
|
signIn,
|
||||||
|
signUp,
|
||||||
|
}: SignUpPanelProperties): JSX.Element => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
const signup = (e: React.SyntheticEvent<HTMLButtonElement>) => {
|
const signup = async (
|
||||||
e.preventDefault();
|
event: React.SyntheticEvent<HTMLButtonElement>
|
||||||
|
): Promise<void> => {
|
||||||
|
event.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.
|
||||||
signUp({
|
await signUp({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
toast.error(result.error.message);
|
toast.error(result.error.message);
|
||||||
} else if (result.data?.confirmation_sent_at) {
|
} else if (result.user?.confirmation_sent_at) {
|
||||||
console.log(result.data.confirmation_sent_at);
|
|
||||||
toast.success(
|
toast.success(
|
||||||
'A confirmation email has been sent to you, watch your mailbox!'
|
'A confirmation email has been sent to you, watch your mailbox!'
|
||||||
);
|
);
|
||||||
} else if (result.data) {
|
} else if (result.user) {
|
||||||
router.push('/');
|
void router.push('/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -78,7 +87,7 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
id="loginBtn"
|
id="loginBtn"
|
||||||
className="w-full btn btn-primary"
|
className="w-full btn btn-primary"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
signup(event);
|
void signup(event);
|
||||||
}}>
|
}}>
|
||||||
Sign Up
|
Sign Up
|
||||||
</button>
|
</button>
|
||||||
@@ -94,7 +103,7 @@ const SignUpPanel = ({ signIn, signUp }: SignUpPanelProps): JSX.Element => {
|
|||||||
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
className="flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border rounded-md focus:outline-none border-base-200 group hover:bg-base-300"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
signIn({ provider: 'google' });
|
void signIn({ provider: 'google' });
|
||||||
}}>
|
}}>
|
||||||
<div className="text-base-content">
|
<div className="text-base-content">
|
||||||
<IoLogoGoogle />
|
<IoLogoGoogle />
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -7,7 +7,8 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"eslint": "eslint . --fix",
|
"eslint": "eslint . --fix",
|
||||||
"test": "playwright test"
|
"test": "playwright test",
|
||||||
|
"update-types": "npx openapi-typescript https://vwkxoczhqygxvqzbhkmj.supabase.co/rest/v1/?apikey=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzMDQwNzE1NCwiZXhwIjoxOTQ1OTgzMTU0fQ.VsWbay8ZrhDe3-elrlh7EGtJfW1INXlUywjeh6h2xgc --output types/database/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.4.2",
|
"@headlessui/react": "^1.4.2",
|
||||||
@@ -16,7 +17,6 @@
|
|||||||
"@supabase/gotrue-js": "^1.21.7",
|
"@supabase/gotrue-js": "^1.21.7",
|
||||||
"@supabase/supabase-js": "^1.29.1",
|
"@supabase/supabase-js": "^1.29.1",
|
||||||
"@types/node": "^17.0.4",
|
"@types/node": "^17.0.4",
|
||||||
"@types/react": "^17.0.38",
|
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -30,8 +30,7 @@
|
|||||||
"react-feather": "^2.0.9",
|
"react-feather": "^2.0.9",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-toastify": "^8.1.0",
|
"react-toastify": "^8.1.0",
|
||||||
"stripe": "^8.195.0",
|
"stripe": "^8.195.0"
|
||||||
"typescript": "^4.5.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/eslint-plugin-next": "^12.0.7",
|
"@next/eslint-plugin-next": "^12.0.7",
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express-rate-limit": "^5.1.3",
|
"@types/express-rate-limit": "^5.1.3",
|
||||||
"@types/micro": "^7.3.6",
|
"@types/micro": "^7.3.6",
|
||||||
|
"@types/react": "^17.0.38",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.4.0",
|
||||||
"eslint": "^8.5.0",
|
"eslint": "^8.5.0",
|
||||||
@@ -53,8 +53,11 @@
|
|||||||
"eslint-plugin-react": "^7.28.0",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||||
|
"eslint-plugin-sonarjs": "^0.11.0",
|
||||||
|
"eslint-plugin-unicorn": "^40.0.0",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"tailwindcss": "^3.0.7"
|
"tailwindcss": "^3.0.7",
|
||||||
|
"typescript": "^4.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,11 +9,9 @@ import Document, {
|
|||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
static async getInitialProps(
|
static async getInitialProps(
|
||||||
ctx: DocumentContext
|
context_: DocumentContext
|
||||||
): Promise<DocumentInitialProps> {
|
): Promise<DocumentInitialProps> {
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
return await Document.getInitialProps(context_);
|
||||||
|
|
||||||
return initialProps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import { supabase } from 'utils/supabaseClient';
|
import { supabase } from 'utils/supabaseClient';
|
||||||
|
|
||||||
export default async function handler(
|
export default function handler(
|
||||||
req: NextApiRequest,
|
request: NextApiRequest,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> {
|
): void {
|
||||||
supabase.auth.api.setAuthCookie(req, res);
|
supabase.auth.api.setAuthCookie(request, response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ import { supabase } from 'utils/supabaseClient';
|
|||||||
|
|
||||||
// Example of how to verify and get user data server-side.
|
// Example of how to verify and get user data server-side.
|
||||||
const getUser = async (
|
const getUser = async (
|
||||||
req: NextApiRequest,
|
request: NextApiRequest,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const token = req.headers.token;
|
const token = request.headers.token;
|
||||||
|
|
||||||
if (typeof token !== 'string') {
|
if (typeof token !== 'string') {
|
||||||
return res.status(401).json({ error: 'Missing auth token.' });
|
return response.status(401).json({ error: 'Missing auth token.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const { data: user, error } = await supabase.auth.api.getUser(token);
|
const { data: user, error } = await supabase.auth.api.getUser(token);
|
||||||
|
|
||||||
if (error) return res.status(401).json({ error: error.message });
|
if (error) return response.status(401).json({ error: error.message });
|
||||||
return res.status(200).json(user);
|
return response.status(200).json(user);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,15 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import Cors from 'cors';
|
import Cors from 'cors';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import initMiddleware from 'utils/init-middleware';
|
import initMiddleware from 'utils/initMiddleware';
|
||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
|
|
||||||
|
interface Request extends NextApiRequest {
|
||||||
|
body: {
|
||||||
|
mail: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
externalResolver: true,
|
externalResolver: true,
|
||||||
@@ -19,44 +25,44 @@ const cors = initMiddleware(
|
|||||||
|
|
||||||
const limiter = initMiddleware(
|
const limiter = initMiddleware(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 30000, // 30sec
|
windowMs: 30_000, // 30sec
|
||||||
max: 2, // Max 2 request per 30 sec
|
max: 2, // Max 2 request per 30 sec
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
request: Request,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(request, response);
|
||||||
await limiter(req, res);
|
await limiter(request, response);
|
||||||
if (req.method === 'PUT') {
|
if (request.method === 'PUT') {
|
||||||
axios
|
axios
|
||||||
.put(
|
.put(
|
||||||
'https://api.sendgrid.com/v3/marketing/contacts',
|
'https://api.sendgrid.com/v3/marketing/contacts',
|
||||||
{
|
{
|
||||||
contacts: [{ email: `${req.body.mail}` }],
|
contacts: [{ email: `${request.body.mail}` }],
|
||||||
list_ids: [process.env.SENDGRID_MAILING_ID],
|
list_ids: [process.env.SENDGRID_MAILING_ID],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
|
Authorization: `Bearer ${process.env.SENDGRID_SECRET || ''}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
res.status(200).send({
|
response.status(200).send({
|
||||||
message:
|
message:
|
||||||
'Your email has been succesfully added to the mailing list. Welcome 👋',
|
'Your email has been succesfully added to the mailing list. Welcome 👋',
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((error) => {
|
||||||
res.status(500).send({
|
response.status(500).send({
|
||||||
message:
|
message:
|
||||||
'Oups, there was a problem with your subscription, please try again or contact us',
|
'Oups, there was a problem with your subscription, please try again or contact us',
|
||||||
error: err,
|
error: error as string,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,31 +7,38 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import sgMail from '@sendgrid/mail';
|
import sgMail from '@sendgrid/mail';
|
||||||
|
|
||||||
const sendGrid = async (
|
interface Request extends NextApiRequest {
|
||||||
req: NextApiRequest,
|
body: {
|
||||||
res: NextApiResponse
|
name: string;
|
||||||
): Promise<void> => {
|
email: string;
|
||||||
if (req.method === 'POST') {
|
message: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendGrid = (request: Request, response: NextApiResponse): void => {
|
||||||
|
if (request.method === 'POST') {
|
||||||
sgMail.setApiKey(process.env.SENDGRID_SECRET || '');
|
sgMail.setApiKey(process.env.SENDGRID_SECRET || '');
|
||||||
|
|
||||||
const msg = {
|
const message = {
|
||||||
to: process.env.SENDGRID_MAILTO || '', // Change to your recipient
|
to: process.env.SENDGRID_MAILTO || '', // Change to your recipient
|
||||||
from: process.env.SENDGRID_MAILFROM || '', // Change to your verified sender
|
from: process.env.SENDGRID_MAILFROM || '', // Change to your verified sender
|
||||||
subject: `[${process.env.NEXT_PUBLIC_TITLE}] New message from ${req.body.name}`,
|
subject: `[${process.env.NEXT_PUBLIC_TITLE || ''}] New message from ${
|
||||||
text: req.body.message,
|
request.body.name
|
||||||
reply_to: req.body.email,
|
}`,
|
||||||
|
text: request.body.message,
|
||||||
|
reply_to: request.body.email,
|
||||||
};
|
};
|
||||||
|
|
||||||
sgMail
|
sgMail
|
||||||
.send(msg)
|
.send(message)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
res
|
response
|
||||||
.status(200)
|
.status(200)
|
||||||
.send({ message: 'Your email has been sent', success: true });
|
.send({ message: 'Your email has been sent', success: true });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error: string) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
res.status(500).send({
|
response.status(500).send({
|
||||||
message: 'There was an issue with your email... please retry',
|
message: 'There was an issue with your email... please retry',
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import Cors from 'cors';
|
import Cors from 'cors';
|
||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
import initMiddleware from 'utils/init-middleware';
|
import initMiddleware from 'utils/initMiddleware';
|
||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
|
|
||||||
const cors = initMiddleware(
|
const cors = initMiddleware(
|
||||||
@@ -13,10 +13,23 @@ const cors = initMiddleware(
|
|||||||
|
|
||||||
const limiter = initMiddleware(
|
const limiter = initMiddleware(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 30000, // 30sec
|
windowMs: 30_000, // 30sec
|
||||||
max: 4, // Max 4 request per 30 sec
|
max: 4, // Max 4 request per 30 sec
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface Request extends NextApiRequest {
|
||||||
|
body: {
|
||||||
|
customerId: string;
|
||||||
|
email: string;
|
||||||
|
pay_mode: Stripe.Checkout.SessionCreateParams.Mode;
|
||||||
|
userId: string;
|
||||||
|
priceId: string;
|
||||||
|
};
|
||||||
|
headers: {
|
||||||
|
origin: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
// Set your secret key. Remember to switch to your live secret key in production.
|
// Set your secret key. Remember to switch to your live secret key in production.
|
||||||
// See your keys here: https://dashboard.stripe.com/apikeys
|
// See your keys here: https://dashboard.stripe.com/apikeys
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
||||||
@@ -24,26 +37,26 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
request: Request,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(request, response);
|
||||||
await limiter(req, res);
|
await limiter(request, response);
|
||||||
if (req.method === 'POST') {
|
if (request.method === 'POST') {
|
||||||
const { priceId } = req.body;
|
const { priceId, customerId, pay_mode, userId, email } = request.body;
|
||||||
|
|
||||||
// See https://stripe.com/docs/api/checkout/sessions/create
|
// See https://stripe.com/docs/api/checkout/sessions/create
|
||||||
// for additional parameters to pass.
|
// for additional parameters to pass.
|
||||||
try {
|
try {
|
||||||
const session = req.body.customerId
|
const session = customerId
|
||||||
? await stripe.checkout.sessions.create({
|
? await stripe.checkout.sessions.create({
|
||||||
mode: req.body.pay_mode,
|
mode: pay_mode,
|
||||||
payment_method_types: ['card'],
|
payment_method_types: ['card'],
|
||||||
client_reference_id: req.body.userId,
|
client_reference_id: userId,
|
||||||
metadata: {
|
metadata: {
|
||||||
priceId: req.body.priceId,
|
priceId: priceId,
|
||||||
},
|
},
|
||||||
customer: req.body.customerId,
|
customer: customerId,
|
||||||
line_items: [
|
line_items: [
|
||||||
{
|
{
|
||||||
price: priceId,
|
price: priceId,
|
||||||
@@ -54,16 +67,16 @@ export default async function handler(
|
|||||||
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
|
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
|
||||||
// the actual Session ID is returned in the query parameter when your customer
|
// the actual Session ID is returned in the query parameter when your customer
|
||||||
// is redirected to the success page.
|
// is redirected to the success page.
|
||||||
success_url: `${req.headers.origin}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
|
success_url: `${request.headers.origin}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
|
||||||
cancel_url: `${req.headers.origin}/pricing`,
|
cancel_url: `${request.headers.origin}/pricing`,
|
||||||
})
|
})
|
||||||
: await stripe.checkout.sessions.create({
|
: await stripe.checkout.sessions.create({
|
||||||
mode: 'subscription',
|
mode: 'subscription',
|
||||||
payment_method_types: ['card'],
|
payment_method_types: ['card'],
|
||||||
customer_email: req.body.email,
|
customer_email: email,
|
||||||
client_reference_id: req.body.userId,
|
client_reference_id: userId,
|
||||||
metadata: {
|
metadata: {
|
||||||
priceId: req.body.priceId,
|
priceId: priceId,
|
||||||
},
|
},
|
||||||
line_items: [
|
line_items: [
|
||||||
{
|
{
|
||||||
@@ -75,16 +88,16 @@ export default async function handler(
|
|||||||
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
|
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
|
||||||
// the actual Session ID is returned in the query parameter when your customer
|
// the actual Session ID is returned in the query parameter when your customer
|
||||||
// is redirected to the success page.
|
// is redirected to the success page.
|
||||||
success_url: `${req.headers.origin}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
|
success_url: `${request.headers.origin}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
|
||||||
cancel_url: `${req.headers.origin}/pricing`,
|
cancel_url: `${request.headers.origin}/pricing`,
|
||||||
});
|
});
|
||||||
res.status(200).send({ url: session.url });
|
response.status(200).send({ url: session.url });
|
||||||
} catch (e: unknown) {
|
} catch (error: unknown) {
|
||||||
res.status(400);
|
response.status(400);
|
||||||
if (e instanceof Error) {
|
if (error instanceof Error) {
|
||||||
return res.send({
|
return response.send({
|
||||||
error: {
|
error: {
|
||||||
message: e.message,
|
message: error.message,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,15 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import Cors from 'cors';
|
import Cors from 'cors';
|
||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
import initMiddleware from 'utils/init-middleware';
|
import initMiddleware from 'utils/initMiddleware';
|
||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
|
|
||||||
|
interface Request extends NextApiRequest {
|
||||||
|
body: {
|
||||||
|
customerId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const cors = initMiddleware(
|
const cors = initMiddleware(
|
||||||
Cors({
|
Cors({
|
||||||
methods: ['POST', 'PUT'],
|
methods: ['POST', 'PUT'],
|
||||||
@@ -16,7 +22,7 @@ const cors = initMiddleware(
|
|||||||
|
|
||||||
const limiter = initMiddleware(
|
const limiter = initMiddleware(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 30000, // 30sec
|
windowMs: 30_000, // 30sec
|
||||||
max: 150, // Max 4 request per 30 sec
|
max: 150, // Max 4 request per 30 sec
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -27,18 +33,20 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
request: Request,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(request, response);
|
||||||
await limiter(req, res);
|
await limiter(request, response);
|
||||||
if (req.method === 'POST') {
|
if (request.method === 'POST') {
|
||||||
const returnUrl = `${req.headers.origin}/dashboard`; // Stripe will return to the dashboard, you can change it
|
const returnUrl = `${
|
||||||
|
request.headers.origin ? request.headers.origin : '/'
|
||||||
|
}/dashboard`; // Stripe will return to the dashboard, you can change it
|
||||||
|
|
||||||
const portalsession = await stripe.billingPortal.sessions.create({
|
const portalsession = await stripe.billingPortal.sessions.create({
|
||||||
customer: req.body.customerId,
|
customer: request.body.customerId,
|
||||||
return_url: returnUrl,
|
return_url: returnUrl,
|
||||||
});
|
});
|
||||||
res.status(200).send({ url: portalsession.url });
|
response.status(200).send({ url: portalsession.url });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ SupaNexTail use only 2 webhooks. Stripe have a lot more,
|
|||||||
you can check it here https://stripe.com/docs/webhooks
|
you can check it here https://stripe.com/docs/webhooks
|
||||||
BE SURE TO SETUP YOUR WEBHOOKS IN YOUR DASHBOARD!
|
BE SURE TO SETUP YOUR WEBHOOKS IN YOUR DASHBOARD!
|
||||||
If you want to test it locally, you'll need the stripe CLI and use this command line:
|
If you want to test it locally, you'll need the stripe CLI and use this command line:
|
||||||
stripe listen --forward-to localhost:3000/api/stripe/stripe-webhook
|
stripe listen --forward-to localhost:3000/api/stripe/webhook
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
@@ -12,7 +12,7 @@ import Cors from 'cors';
|
|||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
import { buffer } from 'micro';
|
import { buffer } from 'micro';
|
||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
import initMiddleware from 'utils/init-middleware';
|
import initMiddleware from 'utils/initMiddleware';
|
||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
@@ -39,7 +39,7 @@ const supabase = createClient(
|
|||||||
|
|
||||||
const limiter = initMiddleware(
|
const limiter = initMiddleware(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 30000, // 30sec
|
windowMs: 30_000, // 30sec
|
||||||
max: 150, // Max 150 request per 30 sec
|
max: 150, // Max 150 request per 30 sec
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -52,18 +52,18 @@ const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
request: NextApiRequest,
|
||||||
res: NextApiResponse
|
response: NextApiResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await cors(req, res);
|
await cors(request, response);
|
||||||
await limiter(req, res);
|
await limiter(request, response);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (request.method === 'POST') {
|
||||||
// Retrieve the event by verifying the signature using the raw body and secret.
|
// Retrieve the event by verifying the signature using the raw body and secret.
|
||||||
let event: Stripe.Event;
|
let event: Stripe.Event;
|
||||||
const buf = await buffer(req);
|
const buf = await buffer(request);
|
||||||
|
|
||||||
const sig = req.headers['stripe-signature'] as string;
|
const sig = request.headers['stripe-signature'] as string;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
event = stripe.webhooks.constructEvent(
|
event = stripe.webhooks.constructEvent(
|
||||||
@@ -71,13 +71,13 @@ export default async function handler(
|
|||||||
sig,
|
sig,
|
||||||
process.env.STRIPE_WEBHOOK || ''
|
process.env.STRIPE_WEBHOOK || ''
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
console.log(err);
|
console.log(error);
|
||||||
console.log(`⚠️ Webhook signature verification failed.`);
|
console.log(`⚠️ Webhook signature verification failed.`);
|
||||||
console.log(
|
console.log(
|
||||||
`⚠️ Check the env file and enter the correct webhook secret.`
|
`⚠️ Check the env file and enter the correct webhook secret.`
|
||||||
);
|
);
|
||||||
return res.send(400);
|
return response.send(400);
|
||||||
}
|
}
|
||||||
// Extract the object from the event.
|
// Extract the object from the event.
|
||||||
const dataObject = event.data.object as {
|
const dataObject = event.data.object as {
|
||||||
@@ -118,7 +118,7 @@ export default async function handler(
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
.then()
|
.then()
|
||||||
.then(null, (err) => console.log('err: ', err)); // catch
|
.then(undefined, (error) => console.log('err:', error)); // catch
|
||||||
} else if (subscriptions?.length && subscriptions?.length > 0) {
|
} else if (subscriptions?.length && subscriptions?.length > 0) {
|
||||||
await supabase
|
await supabase
|
||||||
.from('subscriptions')
|
.from('subscriptions')
|
||||||
@@ -130,7 +130,7 @@ export default async function handler(
|
|||||||
})
|
})
|
||||||
.eq('id', dataObject.client_reference_id)
|
.eq('id', dataObject.client_reference_id)
|
||||||
.then()
|
.then()
|
||||||
.then(null, (err) => console.log('err: ', err)); // catch
|
.then(undefined, (error) => console.log('err:', error)); // catch
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'customer.subscription.deleted':
|
case 'customer.subscription.deleted':
|
||||||
@@ -139,7 +139,7 @@ export default async function handler(
|
|||||||
.update({ paid_user: false })
|
.update({ paid_user: false })
|
||||||
.eq('customer_id', dataObject.customer)
|
.eq('customer_id', dataObject.customer)
|
||||||
.then()
|
.then()
|
||||||
.then(null, (err) => console.log('err: ', err)); // catch
|
.then(undefined, (error) => console.log('err:', error)); // catch
|
||||||
break;
|
break;
|
||||||
case 'invoice.payment_failed':
|
case 'invoice.payment_failed':
|
||||||
// If the payment fails or the customer does not have a valid payment method,
|
// If the payment fails or the customer does not have a valid payment method,
|
||||||
@@ -157,6 +157,6 @@ export default async function handler(
|
|||||||
default:
|
default:
|
||||||
// Unexpected event type
|
// Unexpected event type
|
||||||
}
|
}
|
||||||
res.send(200);
|
response.send(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,12 @@ import { NextSeo } from 'next-seo';
|
|||||||
const ContactPage = (): JSX.Element => (
|
const ContactPage = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Contact`}
|
title={`${
|
||||||
description={`This is the contact page for ${process.env.NEXT_PUBLIC_TITLE}`}
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} | Contact`}
|
||||||
|
description={`This is the contact page for ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable unicorn/no-nested-ternary */
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import Dashboard from '../components/Dashboard';
|
import Dashboard from '../components/Dashboard';
|
||||||
@@ -6,6 +7,7 @@ import Layout from 'components/Layout';
|
|||||||
import type { NextPageContext } from 'next';
|
import type { NextPageContext } from 'next';
|
||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
import { definitions } from 'types/database/index';
|
||||||
import { supabase } from '../utils/supabaseClient';
|
import { supabase } from '../utils/supabaseClient';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ const DashboardPage = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If a user is not logged in, return to the homepage
|
// If a user is not logged in, return to the homepage
|
||||||
if (!user) {
|
if (!user) {
|
||||||
router.push('/');
|
void router.push('/');
|
||||||
}
|
}
|
||||||
}, [router, user]);
|
}, [router, user]);
|
||||||
|
|
||||||
@@ -66,7 +68,10 @@ const DashboardPage = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export async function getServerSideProps(context: NextPageContext) {
|
// eslint-disable-next-line unicorn/prevent-abbreviations
|
||||||
|
export async function getServerSideProps(
|
||||||
|
context: NextPageContext
|
||||||
|
): Promise<any> {
|
||||||
const supabaseAdmin = createClient(
|
const supabaseAdmin = createClient(
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL || '',
|
process.env.NEXT_PUBLIC_SUPABASE_URL || '',
|
||||||
process.env.SUPABASE_ADMIN_KEY || ''
|
process.env.SUPABASE_ADMIN_KEY || ''
|
||||||
@@ -81,7 +86,7 @@ export async function getServerSideProps(context: NextPageContext) {
|
|||||||
// If the user exist, you will retrieve the user profile and if he/she's a paid user
|
// If the user exist, you will retrieve the user profile and if he/she's a paid user
|
||||||
if (user) {
|
if (user) {
|
||||||
const { data: plan } = await supabaseAdmin
|
const { data: plan } = await supabaseAdmin
|
||||||
.from('subscriptions')
|
.from<definitions['subscriptions']>('subscriptions')
|
||||||
.select('subscription, paid_user')
|
.select('subscription, paid_user')
|
||||||
.eq('id', user.id)
|
.eq('id', user.id)
|
||||||
.single();
|
.single();
|
||||||
@@ -92,7 +97,7 @@ export async function getServerSideProps(context: NextPageContext) {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const { data: profile } = await supabaseAdmin
|
const { data: profile } = await supabaseAdmin
|
||||||
.from('profiles')
|
.from<definitions['profiles']>('profiles')
|
||||||
.select(`username, website, avatar_url`)
|
.select(`username, website, avatar_url`)
|
||||||
.eq('id', user.id)
|
.eq('id', user.id)
|
||||||
.single();
|
.single();
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import Layout from 'components/Layout';
|
|||||||
const Home = (): JSX.Element => (
|
const Home = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}</title>
|
<title>{`Welcome to ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} 👋`}</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
content="SupaNexTail is a boilerplate for your SaaS, based on Next.js, Supabase, and TailwindCSS"
|
||||||
@@ -20,7 +22,9 @@ const Home = (): JSX.Element => (
|
|||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta
|
<meta
|
||||||
property="og:title"
|
property="og:title"
|
||||||
content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}
|
content={`Welcome to ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} 👋`}
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:description"
|
property="og:description"
|
||||||
@@ -36,7 +40,9 @@ const Home = (): JSX.Element => (
|
|||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="twitter:title"
|
name="twitter:title"
|
||||||
content={`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}
|
content={`Welcome to ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} 👋`}
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="twitter:description"
|
name="twitter:description"
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ const LoginPage = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Auth`}
|
title={`${process.env.NEXT_PUBLIC_TITLE || ''} | Auth`}
|
||||||
description={`This is the auth page for ${process.env.NEXT_PUBLIC_TITLE}`}
|
description={`This is the auth page for ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE || ''
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import Pricing from 'components/Pricing';
|
|||||||
const PricingPage = (): JSX.Element => (
|
const PricingPage = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Pricing`}
|
title={`${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} | Pricing`}
|
||||||
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
||||||
/>
|
/>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import PrivacyPolicy from 'components/PrivacyPolicy';
|
|||||||
const PrivacyPage = (): JSX.Element => (
|
const PrivacyPage = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Privacy Policy`}
|
title={`${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} | Privacy Policy`}
|
||||||
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
||||||
/>
|
/>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -12,8 +12,12 @@ import { NextSeo } from 'next-seo';
|
|||||||
const SignUpPage = (): JSX.Element => (
|
const SignUpPage = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Auth`}
|
title={`${
|
||||||
description={`This is the auth page for ${process.env.NEXT_PUBLIC_TITLE}`}
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} | Auth`}
|
||||||
|
description={`This is the auth page for ${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import Terms from 'components/Terms';
|
|||||||
const TermsPage = (): JSX.Element => (
|
const TermsPage = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={`${process.env.NEXT_PUBLIC_TITLE} | Terms and conditions`}
|
title={`${
|
||||||
|
process.env.NEXT_PUBLIC_TITLE ? process.env.NEXT_PUBLIC_TITLE : ''
|
||||||
|
} | Terms and conditions`}
|
||||||
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
description="SupaNexTail is a boilerplate for your website, based on Next.js, Supabase, and TailwindCSS"
|
||||||
/>
|
/>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
192
pnpm-lock.yaml
generated
192
pnpm-lock.yaml
generated
@@ -31,6 +31,8 @@ specifiers:
|
|||||||
eslint-plugin-react: ^7.28.0
|
eslint-plugin-react: ^7.28.0
|
||||||
eslint-plugin-react-hooks: ^4.3.0
|
eslint-plugin-react-hooks: ^4.3.0
|
||||||
eslint-plugin-simple-import-sort: ^7.0.0
|
eslint-plugin-simple-import-sort: ^7.0.0
|
||||||
|
eslint-plugin-sonarjs: ^0.11.0
|
||||||
|
eslint-plugin-unicorn: ^40.0.0
|
||||||
express-rate-limit: ^6.0.1
|
express-rate-limit: ^6.0.1
|
||||||
micro: ^9.3.4
|
micro: ^9.3.4
|
||||||
next: '>=12.0.7'
|
next: '>=12.0.7'
|
||||||
@@ -53,7 +55,6 @@ dependencies:
|
|||||||
'@supabase/gotrue-js': 1.21.7
|
'@supabase/gotrue-js': 1.21.7
|
||||||
'@supabase/supabase-js': 1.29.1
|
'@supabase/supabase-js': 1.29.1
|
||||||
'@types/node': 17.0.4
|
'@types/node': 17.0.4
|
||||||
'@types/react': 17.0.38
|
|
||||||
'@types/react-dom': 17.0.11
|
'@types/react-dom': 17.0.11
|
||||||
axios: 0.24.0
|
axios: 0.24.0
|
||||||
cors: 2.8.5
|
cors: 2.8.5
|
||||||
@@ -68,7 +69,6 @@ dependencies:
|
|||||||
react-icons: 4.3.1_react@17.0.2
|
react-icons: 4.3.1_react@17.0.2
|
||||||
react-toastify: 8.1.0_react-dom@17.0.2+react@17.0.2
|
react-toastify: 8.1.0_react-dom@17.0.2+react@17.0.2
|
||||||
stripe: 8.195.0
|
stripe: 8.195.0
|
||||||
typescript: 4.5.4
|
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@next/eslint-plugin-next': 12.0.7
|
'@next/eslint-plugin-next': 12.0.7
|
||||||
@@ -76,6 +76,7 @@ devDependencies:
|
|||||||
'@types/cors': 2.8.12
|
'@types/cors': 2.8.12
|
||||||
'@types/express-rate-limit': 5.1.3
|
'@types/express-rate-limit': 5.1.3
|
||||||
'@types/micro': 7.3.6
|
'@types/micro': 7.3.6
|
||||||
|
'@types/react': 17.0.38
|
||||||
'@typescript-eslint/eslint-plugin': 5.8.0_eslint@8.5.0+typescript@4.5.4
|
'@typescript-eslint/eslint-plugin': 5.8.0_eslint@8.5.0+typescript@4.5.4
|
||||||
autoprefixer: 10.4.0_postcss@8.4.5
|
autoprefixer: 10.4.0_postcss@8.4.5
|
||||||
eslint: 8.5.0
|
eslint: 8.5.0
|
||||||
@@ -90,9 +91,12 @@ devDependencies:
|
|||||||
eslint-plugin-react: 7.28.0_eslint@8.5.0
|
eslint-plugin-react: 7.28.0_eslint@8.5.0
|
||||||
eslint-plugin-react-hooks: 4.3.0_eslint@8.5.0
|
eslint-plugin-react-hooks: 4.3.0_eslint@8.5.0
|
||||||
eslint-plugin-simple-import-sort: 7.0.0_eslint@8.5.0
|
eslint-plugin-simple-import-sort: 7.0.0_eslint@8.5.0
|
||||||
|
eslint-plugin-sonarjs: 0.11.0_eslint@8.5.0
|
||||||
|
eslint-plugin-unicorn: 40.0.0_eslint@8.5.0
|
||||||
postcss: 8.4.5
|
postcss: 8.4.5
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
tailwindcss: 3.0.7_16a290f6d0e3717bf6d2667234aebd30
|
tailwindcss: 3.0.7_16a290f6d0e3717bf6d2667234aebd30
|
||||||
|
typescript: 4.5.4
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1099,13 +1103,16 @@ packages:
|
|||||||
/@types/node/17.0.4:
|
/@types/node/17.0.4:
|
||||||
resolution: {integrity: sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==}
|
resolution: {integrity: sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==}
|
||||||
|
|
||||||
|
/@types/normalize-package-data/2.4.1:
|
||||||
|
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/parse-json/4.0.0:
|
/@types/parse-json/4.0.0:
|
||||||
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/prop-types/15.7.4:
|
/@types/prop-types/15.7.4:
|
||||||
resolution: {integrity: sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==}
|
resolution: {integrity: sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/qs/6.9.7:
|
/@types/qs/6.9.7:
|
||||||
resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
|
resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
|
||||||
@@ -1127,11 +1134,9 @@ packages:
|
|||||||
'@types/prop-types': 15.7.4
|
'@types/prop-types': 15.7.4
|
||||||
'@types/scheduler': 0.16.2
|
'@types/scheduler': 0.16.2
|
||||||
csstype: 3.0.9
|
csstype: 3.0.9
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/scheduler/0.16.2:
|
/@types/scheduler/0.16.2:
|
||||||
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
|
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/serve-static/1.13.10:
|
/@types/serve-static/1.13.10:
|
||||||
resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
|
resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
|
||||||
@@ -1699,6 +1704,11 @@ packages:
|
|||||||
node-gyp-build: 4.3.0
|
node-gyp-build: 4.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/builtin-modules/3.2.0:
|
||||||
|
resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/builtin-status-codes/3.0.0:
|
/builtin-status-codes/3.0.0:
|
||||||
resolution: {integrity: sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=}
|
resolution: {integrity: sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -1786,6 +1796,10 @@ packages:
|
|||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ci-info/3.3.0:
|
||||||
|
resolution: {integrity: sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/cipher-base/1.0.4:
|
/cipher-base/1.0.4:
|
||||||
resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
|
resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1797,6 +1811,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==}
|
resolution: {integrity: sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/clean-regexp/1.0.0:
|
||||||
|
resolution: {integrity: sha1-jffHquUf02h06PjQW5GAvBGj/tc=}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dependencies:
|
||||||
|
escape-string-regexp: 1.0.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/clsx/1.1.1:
|
/clsx/1.1.1:
|
||||||
resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==}
|
resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -1982,7 +2003,6 @@ packages:
|
|||||||
|
|
||||||
/csstype/3.0.9:
|
/csstype/3.0.9:
|
||||||
resolution: {integrity: sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==}
|
resolution: {integrity: sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/d/1.0.1:
|
/d/1.0.1:
|
||||||
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
|
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
|
||||||
@@ -2474,6 +2494,38 @@ packages:
|
|||||||
eslint: 8.5.0
|
eslint: 8.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/eslint-plugin-sonarjs/0.11.0_eslint@8.5.0:
|
||||||
|
resolution: {integrity: sha512-ei/WuZiL0wP+qx2KrxKyZs3+eDbxiGAhFSm3GKCOOAUkg+G2ny6TSXDB2j67tvyqHefi+eoQsAgGQvz+nEtIBw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^5.0.0 || ^6.0.0 || ^7.0.0|| ^8.0.0
|
||||||
|
dependencies:
|
||||||
|
eslint: 8.5.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/eslint-plugin-unicorn/40.0.0_eslint@8.5.0:
|
||||||
|
resolution: {integrity: sha512-5GRXISfBk8jMmYk1eeNDw8zSRnWTxBjWkzx2Prre6E2/yLu2twozZ3EomLWCBu9nWms/ZE361BItyMQwfnG1qA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: '>=7.32.0'
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-validator-identifier': 7.15.7
|
||||||
|
ci-info: 3.3.0
|
||||||
|
clean-regexp: 1.0.0
|
||||||
|
eslint: 8.5.0
|
||||||
|
eslint-utils: 3.0.0_eslint@8.5.0
|
||||||
|
esquery: 1.4.0
|
||||||
|
indent-string: 4.0.0
|
||||||
|
is-builtin-module: 3.1.0
|
||||||
|
lodash: 4.17.21
|
||||||
|
pluralize: 8.0.0
|
||||||
|
read-pkg-up: 7.0.1
|
||||||
|
regexp-tree: 0.1.24
|
||||||
|
safe-regex: 2.1.1
|
||||||
|
semver: 7.3.5
|
||||||
|
strip-indent: 3.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/eslint-scope/5.1.1:
|
/eslint-scope/5.1.1:
|
||||||
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
@@ -2725,7 +2777,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
locate-path: 5.0.0
|
locate-path: 5.0.0
|
||||||
path-exists: 4.0.0
|
path-exists: 4.0.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/flat-cache/3.0.4:
|
/flat-cache/3.0.4:
|
||||||
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
||||||
@@ -2929,6 +2980,10 @@ packages:
|
|||||||
minimalistic-crypto-utils: 1.0.1
|
minimalistic-crypto-utils: 1.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hosted-git-info/2.8.9:
|
||||||
|
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/http-errors/1.6.2:
|
/http-errors/1.6.2:
|
||||||
resolution: {integrity: sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=}
|
resolution: {integrity: sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -3032,6 +3087,11 @@ packages:
|
|||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/indent-string/4.0.0:
|
||||||
|
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/inflight/1.0.6:
|
/inflight/1.0.6:
|
||||||
resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
|
resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3088,6 +3148,13 @@ packages:
|
|||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
has-tostringtag: 1.0.0
|
has-tostringtag: 1.0.0
|
||||||
|
|
||||||
|
/is-builtin-module/3.1.0:
|
||||||
|
resolution: {integrity: sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
builtin-modules: 3.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/is-callable/1.2.4:
|
/is-callable/1.2.4:
|
||||||
resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==}
|
resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3364,7 +3431,6 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 4.1.0
|
p-locate: 4.1.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.merge/4.6.2:
|
/lodash.merge/4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
@@ -3374,6 +3440,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=}
|
resolution: {integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash/4.17.21:
|
||||||
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/loose-envify/1.4.0:
|
/loose-envify/1.4.0:
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3444,6 +3514,11 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/min-indent/1.0.1:
|
||||||
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/minimalistic-assert/1.0.1:
|
/minimalistic-assert/1.0.1:
|
||||||
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
|
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -3612,6 +3687,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==}
|
resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/normalize-package-data/2.5.0:
|
||||||
|
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||||
|
dependencies:
|
||||||
|
hosted-git-info: 2.8.9
|
||||||
|
resolve: 1.20.0
|
||||||
|
semver: 5.7.1
|
||||||
|
validate-npm-package-license: 3.0.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/normalize-path/3.0.0:
|
/normalize-path/3.0.0:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -3731,7 +3815,6 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dependencies:
|
dependencies:
|
||||||
p-try: 2.2.0
|
p-try: 2.2.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/p-limit/3.1.0:
|
/p-limit/3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
@@ -3752,7 +3835,6 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit: 2.3.0
|
p-limit: 2.3.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/p-try/1.0.0:
|
/p-try/1.0.0:
|
||||||
resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=}
|
resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=}
|
||||||
@@ -3762,7 +3844,6 @@ packages:
|
|||||||
/p-try/2.2.0:
|
/p-try/2.2.0:
|
||||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/pako/1.0.11:
|
/pako/1.0.11:
|
||||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||||
@@ -3807,7 +3888,6 @@ packages:
|
|||||||
/path-exists/4.0.0:
|
/path-exists/4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/path-is-absolute/1.0.1:
|
/path-is-absolute/1.0.1:
|
||||||
resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
|
resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
|
||||||
@@ -3910,6 +3990,11 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pluralize/8.0.0:
|
||||||
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pngjs/4.0.1:
|
/pngjs/4.0.1:
|
||||||
resolution: {integrity: sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==}
|
resolution: {integrity: sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
@@ -4183,6 +4268,25 @@ packages:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/read-pkg-up/7.0.1:
|
||||||
|
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
find-up: 4.1.0
|
||||||
|
read-pkg: 5.2.0
|
||||||
|
type-fest: 0.8.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/read-pkg/5.2.0:
|
||||||
|
resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
'@types/normalize-package-data': 2.4.1
|
||||||
|
normalize-package-data: 2.5.0
|
||||||
|
parse-json: 5.2.0
|
||||||
|
type-fest: 0.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/readable-stream/3.6.0:
|
/readable-stream/3.6.0:
|
||||||
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
|
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -4213,6 +4317,11 @@ packages:
|
|||||||
/regenerator-runtime/0.13.9:
|
/regenerator-runtime/0.13.9:
|
||||||
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
|
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
|
||||||
|
|
||||||
|
/regexp-tree/0.1.24:
|
||||||
|
resolution: {integrity: sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/regexp.prototype.flags/1.3.1:
|
/regexp.prototype.flags/1.3.1:
|
||||||
resolution: {integrity: sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==}
|
resolution: {integrity: sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -4287,6 +4396,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/safe-regex/2.1.1:
|
||||||
|
resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==}
|
||||||
|
dependencies:
|
||||||
|
regexp-tree: 0.1.24
|
||||||
|
dev: true
|
||||||
|
|
||||||
/safer-buffer/2.1.2:
|
/safer-buffer/2.1.2:
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -4298,6 +4413,11 @@ packages:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/semver/5.7.1:
|
||||||
|
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/semver/6.3.0:
|
/semver/6.3.0:
|
||||||
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
|
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -4429,6 +4549,28 @@ packages:
|
|||||||
whatwg-url: 7.1.0
|
whatwg-url: 7.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/spdx-correct/3.1.1:
|
||||||
|
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
||||||
|
dependencies:
|
||||||
|
spdx-expression-parse: 3.0.1
|
||||||
|
spdx-license-ids: 3.0.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-exceptions/2.3.0:
|
||||||
|
resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-expression-parse/3.0.1:
|
||||||
|
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
|
||||||
|
dependencies:
|
||||||
|
spdx-exceptions: 2.3.0
|
||||||
|
spdx-license-ids: 3.0.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-license-ids/3.0.11:
|
||||||
|
resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/stack-utils/2.0.5:
|
/stack-utils/2.0.5:
|
||||||
resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==}
|
resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4516,6 +4658,13 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/strip-indent/3.0.0:
|
||||||
|
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
min-indent: 1.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/strip-json-comments/3.1.1:
|
/strip-json-comments/3.1.1:
|
||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -4694,11 +4843,21 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/type-fest/0.6.0:
|
||||||
|
resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/type-fest/0.7.1:
|
/type-fest/0.7.1:
|
||||||
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
|
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/type-fest/0.8.1:
|
||||||
|
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/type/1.2.0:
|
/type/1.2.0:
|
||||||
resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
|
resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -4717,7 +4876,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==}
|
resolution: {integrity: sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=4.2.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/unbox-primitive/1.0.1:
|
/unbox-primitive/1.0.1:
|
||||||
resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
|
resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==}
|
||||||
@@ -4773,6 +4932,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
|
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/validate-npm-package-license/3.0.4:
|
||||||
|
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||||
|
dependencies:
|
||||||
|
spdx-correct: 3.1.1
|
||||||
|
spdx-expression-parse: 3.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vary/1.1.2:
|
/vary/1.1.2:
|
||||||
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
|
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable unicorn/prefer-module */
|
||||||
// If you want to use other PostCSS plugins, see the following:
|
// If you want to use other PostCSS plugins, see the following:
|
||||||
// https://tailwindcss.com/docs/using-with-preprocessors
|
// https://tailwindcss.com/docs/using-with-preprocessors
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,76 +1,77 @@
|
|||||||
|
/* eslint-disable unicorn/prefer-module */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'jit',
|
mode: 'jit',
|
||||||
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
|
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
|
||||||
darkMode: false, // or 'media' or 'class'
|
darkMode: false, // or 'media' or 'class'
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
title: ['Poppins'],
|
title: ['Poppins'],
|
||||||
body: ['Inter'],
|
body: ['Inter'],
|
||||||
},
|
},
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('daisyui')],
|
||||||
daisyui: {
|
daisyui: {
|
||||||
themes: [
|
themes: [
|
||||||
{
|
{
|
||||||
supaTheme: {
|
supaTheme: {
|
||||||
primary: '#00B8F0',
|
primary: '#00B8F0',
|
||||||
'primary-focus': '#009de0',
|
'primary-focus': '#009de0',
|
||||||
'primary-content': '#ffffff',
|
'primary-content': '#ffffff',
|
||||||
|
|
||||||
secondary: '#f03800',
|
secondary: '#f03800',
|
||||||
'secondary-focus': '#e22f00',
|
'secondary-focus': '#e22f00',
|
||||||
'secondary-content': '#ffffff',
|
'secondary-content': '#ffffff',
|
||||||
|
|
||||||
accent: '#00f0b0',
|
accent: '#00f0b0',
|
||||||
'accent-focus': '#00e28a',
|
'accent-focus': '#00e28a',
|
||||||
'accent-content': '#ffffff',
|
'accent-content': '#ffffff',
|
||||||
|
|
||||||
neutral: '#3d4451',
|
neutral: '#3d4451',
|
||||||
'neutral-focus': '#2a2e37',
|
'neutral-focus': '#2a2e37',
|
||||||
'neutral-content': '#ffffff',
|
'neutral-content': '#ffffff',
|
||||||
|
|
||||||
'base-100': '#ffffff',
|
'base-100': '#ffffff',
|
||||||
'base-200': '#767676',
|
'base-200': '#767676',
|
||||||
'base-300': '#d1d5db',
|
'base-300': '#d1d5db',
|
||||||
'base-content': '#1f2937',
|
'base-content': '#1f2937',
|
||||||
|
|
||||||
info: '#2094f3' /* Info */,
|
info: '#2094f3' /* Info */,
|
||||||
success: '#009485' /* Success */,
|
success: '#009485' /* Success */,
|
||||||
warning: '#ff9900' /* Warning */,
|
warning: '#ff9900' /* Warning */,
|
||||||
error: '#ff5724' /* Error */,
|
error: '#ff5724' /* Error */,
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
primary: '#00B8F0',
|
primary: '#00B8F0',
|
||||||
'primary-focus': '#009de0',
|
'primary-focus': '#009de0',
|
||||||
'primary-content': '#ffffff',
|
'primary-content': '#ffffff',
|
||||||
|
|
||||||
secondary: '#f03800',
|
secondary: '#f03800',
|
||||||
'secondary-focus': '#e22f00',
|
'secondary-focus': '#e22f00',
|
||||||
'secondary-content': '#ffffff',
|
'secondary-content': '#ffffff',
|
||||||
|
|
||||||
accent: '#00f0b0',
|
accent: '#00f0b0',
|
||||||
'accent-focus': '#00e28a',
|
'accent-focus': '#00e28a',
|
||||||
'accent-content': '#ffffff',
|
'accent-content': '#ffffff',
|
||||||
|
|
||||||
neutral: '#3d4451',
|
neutral: '#3d4451',
|
||||||
'neutral-focus': '#2a2e37',
|
'neutral-focus': '#2a2e37',
|
||||||
'neutral-content': '#ffffff',
|
'neutral-content': '#ffffff',
|
||||||
|
|
||||||
'base-100': '#2A2E37',
|
'base-100': '#2A2E37',
|
||||||
'base-200': '#EBECF0',
|
'base-200': '#EBECF0',
|
||||||
'base-300': '#16181D',
|
'base-300': '#16181D',
|
||||||
'base-content': '#EBECF0',
|
'base-content': '#EBECF0',
|
||||||
|
|
||||||
info: '#2094f3',
|
info: '#2094f3',
|
||||||
success: '#009485',
|
success: '#009485',
|
||||||
warning: '#ff9900',
|
warning: '#ff9900',
|
||||||
error: '#ff5724',
|
error: '#ff5724',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||||
/* Module Resolution Options */
|
/* Module Resolution Options */
|
||||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */ /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */ /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
"inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
"inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
/* Advanced Options */
|
/* Advanced Options */
|
||||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||||
@@ -65,9 +65,16 @@
|
|||||||
"include": [
|
"include": [
|
||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx"
|
"**/*.tsx",
|
||||||
|
"utils/AuthContext.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"*.js",
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
".next",
|
||||||
|
"out",
|
||||||
|
"types/*",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
441
types/database/index.ts
Normal file
441
types/database/index.ts
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
/**
|
||||||
|
* This file was auto-generated by openapi-typescript.
|
||||||
|
* Do not make direct changes to the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface paths {
|
||||||
|
'/': {
|
||||||
|
get: {
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'/admin_list': {
|
||||||
|
get: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.admin_list.id'];
|
||||||
|
isadmin?: parameters['rowFilter.admin_list.isadmin'];
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
/** Ordering */
|
||||||
|
order?: parameters['order'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
offset?: parameters['offset'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
limit?: parameters['limit'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
Range?: parameters['range'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
'Range-Unit'?: parameters['rangeUnit'];
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferCount'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: {
|
||||||
|
schema: definitions['admin_list'][];
|
||||||
|
};
|
||||||
|
/** Partial Content */
|
||||||
|
206: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
post: {
|
||||||
|
parameters: {
|
||||||
|
body: {
|
||||||
|
/** admin_list */
|
||||||
|
admin_list?: definitions['admin_list'];
|
||||||
|
};
|
||||||
|
query: {
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** Created */
|
||||||
|
201: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
delete: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.admin_list.id'];
|
||||||
|
isadmin?: parameters['rowFilter.admin_list.isadmin'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
patch: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.admin_list.id'];
|
||||||
|
isadmin?: parameters['rowFilter.admin_list.isadmin'];
|
||||||
|
};
|
||||||
|
body: {
|
||||||
|
/** admin_list */
|
||||||
|
admin_list?: definitions['admin_list'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'/profiles': {
|
||||||
|
get: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.profiles.id'];
|
||||||
|
updated_at?: parameters['rowFilter.profiles.updated_at'];
|
||||||
|
username?: parameters['rowFilter.profiles.username'];
|
||||||
|
avatar_url?: parameters['rowFilter.profiles.avatar_url'];
|
||||||
|
website?: parameters['rowFilter.profiles.website'];
|
||||||
|
customerId?: parameters['rowFilter.profiles.customerId'];
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
/** Ordering */
|
||||||
|
order?: parameters['order'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
offset?: parameters['offset'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
limit?: parameters['limit'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
Range?: parameters['range'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
'Range-Unit'?: parameters['rangeUnit'];
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferCount'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: {
|
||||||
|
schema: definitions['profiles'][];
|
||||||
|
};
|
||||||
|
/** Partial Content */
|
||||||
|
206: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
post: {
|
||||||
|
parameters: {
|
||||||
|
body: {
|
||||||
|
/** profiles */
|
||||||
|
profiles?: definitions['profiles'];
|
||||||
|
};
|
||||||
|
query: {
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** Created */
|
||||||
|
201: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
delete: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.profiles.id'];
|
||||||
|
updated_at?: parameters['rowFilter.profiles.updated_at'];
|
||||||
|
username?: parameters['rowFilter.profiles.username'];
|
||||||
|
avatar_url?: parameters['rowFilter.profiles.avatar_url'];
|
||||||
|
website?: parameters['rowFilter.profiles.website'];
|
||||||
|
customerId?: parameters['rowFilter.profiles.customerId'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
patch: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.profiles.id'];
|
||||||
|
updated_at?: parameters['rowFilter.profiles.updated_at'];
|
||||||
|
username?: parameters['rowFilter.profiles.username'];
|
||||||
|
avatar_url?: parameters['rowFilter.profiles.avatar_url'];
|
||||||
|
website?: parameters['rowFilter.profiles.website'];
|
||||||
|
customerId?: parameters['rowFilter.profiles.customerId'];
|
||||||
|
};
|
||||||
|
body: {
|
||||||
|
/** profiles */
|
||||||
|
profiles?: definitions['profiles'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'/subscriptions': {
|
||||||
|
get: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.subscriptions.id'];
|
||||||
|
customer_id?: parameters['rowFilter.subscriptions.customer_id'];
|
||||||
|
paid_user?: parameters['rowFilter.subscriptions.paid_user'];
|
||||||
|
plan?: parameters['rowFilter.subscriptions.plan'];
|
||||||
|
subscription?: parameters['rowFilter.subscriptions.subscription'];
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
/** Ordering */
|
||||||
|
order?: parameters['order'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
offset?: parameters['offset'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
limit?: parameters['limit'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
Range?: parameters['range'];
|
||||||
|
/** Limiting and Pagination */
|
||||||
|
'Range-Unit'?: parameters['rangeUnit'];
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferCount'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: {
|
||||||
|
schema: definitions['subscriptions'][];
|
||||||
|
};
|
||||||
|
/** Partial Content */
|
||||||
|
206: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
post: {
|
||||||
|
parameters: {
|
||||||
|
body: {
|
||||||
|
/** subscriptions */
|
||||||
|
subscriptions?: definitions['subscriptions'];
|
||||||
|
};
|
||||||
|
query: {
|
||||||
|
/** Filtering Columns */
|
||||||
|
select?: parameters['select'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** Created */
|
||||||
|
201: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
delete: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.subscriptions.id'];
|
||||||
|
customer_id?: parameters['rowFilter.subscriptions.customer_id'];
|
||||||
|
paid_user?: parameters['rowFilter.subscriptions.paid_user'];
|
||||||
|
plan?: parameters['rowFilter.subscriptions.plan'];
|
||||||
|
subscription?: parameters['rowFilter.subscriptions.subscription'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
patch: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
id?: parameters['rowFilter.subscriptions.id'];
|
||||||
|
customer_id?: parameters['rowFilter.subscriptions.customer_id'];
|
||||||
|
paid_user?: parameters['rowFilter.subscriptions.paid_user'];
|
||||||
|
plan?: parameters['rowFilter.subscriptions.plan'];
|
||||||
|
subscription?: parameters['rowFilter.subscriptions.subscription'];
|
||||||
|
};
|
||||||
|
body: {
|
||||||
|
/** subscriptions */
|
||||||
|
subscriptions?: definitions['subscriptions'];
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferReturn'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** No Content */
|
||||||
|
204: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'/rpc/handle_new_user': {
|
||||||
|
post: {
|
||||||
|
parameters: {
|
||||||
|
body: {
|
||||||
|
args: { [key: string]: unknown };
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
/** Preference */
|
||||||
|
Prefer?: parameters['preferParams'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface definitions {
|
||||||
|
admin_list: {
|
||||||
|
/**
|
||||||
|
* Format: uuid
|
||||||
|
* @description Note:
|
||||||
|
* This is a Primary Key.<pk/>
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/** Format: boolean */
|
||||||
|
isadmin?: boolean;
|
||||||
|
};
|
||||||
|
profiles: {
|
||||||
|
/**
|
||||||
|
* Format: uuid
|
||||||
|
* @description Note:
|
||||||
|
* This is a Primary Key.<pk/>
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/** Format: timestamp with time zone */
|
||||||
|
updated_at?: string;
|
||||||
|
/** Format: text */
|
||||||
|
username?: string;
|
||||||
|
/** Format: text */
|
||||||
|
avatar_url?: string;
|
||||||
|
/** Format: text */
|
||||||
|
website?: string;
|
||||||
|
/** Format: text */
|
||||||
|
customerId?: string;
|
||||||
|
};
|
||||||
|
subscriptions: {
|
||||||
|
/**
|
||||||
|
* Format: uuid
|
||||||
|
* @description Note:
|
||||||
|
* This is a Primary Key.<pk/>
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/** Format: text */
|
||||||
|
customer_id?: string;
|
||||||
|
/** Format: boolean */
|
||||||
|
paid_user?: boolean;
|
||||||
|
/** Format: text */
|
||||||
|
plan?: string;
|
||||||
|
/** Format: text */
|
||||||
|
subscription?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface parameters {
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferParams: 'params=single-object';
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferReturn: 'return=representation' | 'return=minimal' | 'return=none';
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferCount: 'count=none';
|
||||||
|
/** @description Filtering Columns */
|
||||||
|
select: string;
|
||||||
|
/** @description On Conflict */
|
||||||
|
on_conflict: string;
|
||||||
|
/** @description Ordering */
|
||||||
|
order: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
range: string;
|
||||||
|
/**
|
||||||
|
* @description Limiting and Pagination
|
||||||
|
* @default items
|
||||||
|
*/
|
||||||
|
rangeUnit: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
offset: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
limit: string;
|
||||||
|
/** @description admin_list */
|
||||||
|
'body.admin_list': definitions['admin_list'];
|
||||||
|
/** Format: uuid */
|
||||||
|
'rowFilter.admin_list.id': string;
|
||||||
|
/** Format: boolean */
|
||||||
|
'rowFilter.admin_list.isadmin': string;
|
||||||
|
/** @description profiles */
|
||||||
|
'body.profiles': definitions['profiles'];
|
||||||
|
/** Format: uuid */
|
||||||
|
'rowFilter.profiles.id': string;
|
||||||
|
/** Format: timestamp with time zone */
|
||||||
|
'rowFilter.profiles.updated_at': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.profiles.username': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.profiles.avatar_url': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.profiles.website': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.profiles.customerId': string;
|
||||||
|
/** @description subscriptions */
|
||||||
|
'body.subscriptions': definitions['subscriptions'];
|
||||||
|
/** Format: uuid */
|
||||||
|
'rowFilter.subscriptions.id': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.subscriptions.customer_id': string;
|
||||||
|
/** Format: boolean */
|
||||||
|
'rowFilter.subscriptions.paid_user': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.subscriptions.plan': string;
|
||||||
|
/** Format: text */
|
||||||
|
'rowFilter.subscriptions.subscription': string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface operations {}
|
||||||
|
|
||||||
|
export interface external {}
|
||||||
54
types/supabase.ts
Normal file
54
types/supabase.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* This file was auto-generated by openapi-typescript.
|
||||||
|
* Do not make direct changes to the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface paths {
|
||||||
|
'/': {
|
||||||
|
get: {
|
||||||
|
responses: {
|
||||||
|
/** OK */
|
||||||
|
200: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface parameters {
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferParams: 'params=single-object';
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferReturn: 'return=representation' | 'return=minimal' | 'return=none';
|
||||||
|
/**
|
||||||
|
* @description Preference
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
preferCount: 'count=none';
|
||||||
|
/** @description Filtering Columns */
|
||||||
|
select: string;
|
||||||
|
/** @description On Conflict */
|
||||||
|
on_conflict: string;
|
||||||
|
/** @description Ordering */
|
||||||
|
order: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
range: string;
|
||||||
|
/**
|
||||||
|
* @description Limiting and Pagination
|
||||||
|
* @default items
|
||||||
|
*/
|
||||||
|
rangeUnit: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
offset: string;
|
||||||
|
/** @description Limiting and Pagination */
|
||||||
|
limit: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface operations {}
|
||||||
|
|
||||||
|
export interface external {}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { supabase } from 'utils/supabaseClient';
|
|
||||||
|
|
||||||
// create a context for authentication
|
|
||||||
const AuthContext = createContext();
|
|
||||||
|
|
||||||
export const AuthProvider = ({ children }) => {
|
|
||||||
const [user, setUser] = useState();
|
|
||||||
const [session, setSession] = useState();
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Check active sessions and sets the user
|
|
||||||
const session = supabase.auth.session();
|
|
||||||
|
|
||||||
setUser(session?.user ?? null);
|
|
||||||
setSession(session ?? null);
|
|
||||||
setLoading(false);
|
|
||||||
|
|
||||||
// Listen for changes on auth state (logged in, signed out, etc.)
|
|
||||||
const { data: listener } = supabase.auth.onAuthStateChange(
|
|
||||||
async (event, session) => {
|
|
||||||
if ((event === 'SIGNED_OUT') | (event === 'SIGNED_IN')) {
|
|
||||||
fetch('/api/auth', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: new Headers({ 'Content-Type': 'application/json' }),
|
|
||||||
credentials: 'same-origin',
|
|
||||||
body: JSON.stringify({ event, session }),
|
|
||||||
}).then((res) => res.json());
|
|
||||||
}
|
|
||||||
if (event === 'USER_UPDATED') {
|
|
||||||
}
|
|
||||||
setUser(session?.user ?? null);
|
|
||||||
setSession(session ?? null);
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
listener?.unsubscribe();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Will be passed down to Signup, Login and Dashboard components
|
|
||||||
const value = {
|
|
||||||
signUp: (data) => supabase.auth.signUp(data),
|
|
||||||
signIn: (data) => supabase.auth.signIn(data),
|
|
||||||
signOut: () => supabase.auth.signOut(),
|
|
||||||
resetPassword: (data) => supabase.auth.api.resetPasswordForEmail(data),
|
|
||||||
user,
|
|
||||||
session,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthContext.Provider value={value}>
|
|
||||||
{!loading && children}
|
|
||||||
</AuthContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// export the useAuth hook
|
|
||||||
export const useAuth = () => useContext(AuthContext);
|
|
||||||
108
utils/AuthContext.tsx
Normal file
108
utils/AuthContext.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { ApiError, Session, UserCredentials } from '@supabase/gotrue-js';
|
||||||
|
import {
|
||||||
|
ReactNode,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import { supabase } from 'utils/supabaseClient';
|
||||||
|
|
||||||
|
type authContextType = {
|
||||||
|
user: Session['user'] | undefined;
|
||||||
|
session: Session | null;
|
||||||
|
signOut: () => void;
|
||||||
|
signUp: (data: UserCredentials) => Promise<{
|
||||||
|
user: Session['user'] | null;
|
||||||
|
session: Session | null;
|
||||||
|
error: ApiError | null;
|
||||||
|
}>;
|
||||||
|
resetPassword: (data: string) => Promise<{
|
||||||
|
data: {} | null;
|
||||||
|
error: ApiError | null;
|
||||||
|
}>;
|
||||||
|
signIn: (data: UserCredentials) => Promise<{
|
||||||
|
user: Session['user'] | null;
|
||||||
|
session: Session | null;
|
||||||
|
error: ApiError | null;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Properties = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const authContextDefaultValues: authContextType = {
|
||||||
|
user: undefined,
|
||||||
|
session: null,
|
||||||
|
signOut: () => {},
|
||||||
|
signUp: () => {
|
||||||
|
return Promise.resolve({ user: null, session: null, error: null });
|
||||||
|
},
|
||||||
|
signIn: () => {
|
||||||
|
return Promise.resolve({ user: null, session: null, error: null });
|
||||||
|
},
|
||||||
|
resetPassword: () => {
|
||||||
|
return Promise.resolve({ data: null, error: null });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a context for authentication
|
||||||
|
const AuthContext = createContext(authContextDefaultValues);
|
||||||
|
|
||||||
|
export const AuthProvider = ({ children }: Properties): JSX.Element => {
|
||||||
|
const [user, setUser] = useState(supabase.auth.session()?.user);
|
||||||
|
const [session, setSession] = useState(supabase.auth.session());
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check active sessions and sets the user
|
||||||
|
const session = supabase.auth.session();
|
||||||
|
|
||||||
|
setUser(session?.user);
|
||||||
|
setSession(session);
|
||||||
|
setLoading(false);
|
||||||
|
|
||||||
|
// Listen for changes on auth state (logged in, signed out, etc.)
|
||||||
|
const { data: listener } = supabase.auth.onAuthStateChange(
|
||||||
|
(event, session): void => {
|
||||||
|
if (event === 'SIGNED_OUT' || event === 'SIGNED_IN') {
|
||||||
|
void fetch('/api/auth', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||||
|
credentials: 'same-origin',
|
||||||
|
body: JSON.stringify({ event, session }),
|
||||||
|
}).then((response) => response.json());
|
||||||
|
}
|
||||||
|
setUser(session?.user);
|
||||||
|
setSession(session);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
listener?.unsubscribe();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Will be passed down to Signup, Login and Dashboard components
|
||||||
|
const value = {
|
||||||
|
signUp: (data: UserCredentials) => supabase.auth.signUp(data),
|
||||||
|
signIn: (data: UserCredentials) => supabase.auth.signIn(data),
|
||||||
|
signOut: () => supabase.auth.signOut(),
|
||||||
|
resetPassword: (data: string) =>
|
||||||
|
supabase.auth.api.resetPasswordForEmail(data),
|
||||||
|
user,
|
||||||
|
session,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={value}>
|
||||||
|
{!loading && children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// export the useAuth hook
|
||||||
|
export const useAuth = (): authContextType => useContext(AuthContext);
|
||||||
@@ -4,9 +4,10 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
export default function initMiddleware(middleware: any) {
|
export default function initMiddleware(middleware: any) {
|
||||||
return (req: NextApiRequest, res: NextApiResponse) =>
|
return (request: NextApiRequest, response: NextApiResponse) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
middleware(req, res, (result: any) => {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
middleware(request, response, (result: any) => {
|
||||||
if (result instanceof Error) {
|
if (result instanceof Error) {
|
||||||
return reject(result);
|
return reject(result);
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Stripe, loadStripe } from '@stripe/stripe-js';
|
|||||||
|
|
||||||
let stripePromise: Promise<Stripe | null>;
|
let stripePromise: Promise<Stripe | null>;
|
||||||
const getStripe = (): Promise<Stripe | null> => {
|
const getStripe = (): Promise<Stripe | null> => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
if (!stripePromise) {
|
if (!stripePromise) {
|
||||||
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || '');
|
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
import { definitions } from 'types/database/index';
|
||||||
|
|
||||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||||
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
||||||
@@ -6,12 +7,12 @@ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|||||||
export const supabase = createClient(supabaseUrl || '', supabaseAnonKey || '');
|
export const supabase = createClient(supabaseUrl || '', supabaseAnonKey || '');
|
||||||
|
|
||||||
// Check if a user has a paid plan
|
// Check if a user has a paid plan
|
||||||
export const getSub = async () => {
|
export const getSub = async (): Promise<
|
||||||
|
undefined | definitions['subscriptions']
|
||||||
|
> => {
|
||||||
const { data: subscriptions } = await supabase
|
const { data: subscriptions } = await supabase
|
||||||
.from('subscriptions')
|
.from<definitions['subscriptions']>('subscriptions')
|
||||||
.select('paid_user, plan')
|
.select('paid_user, plan')
|
||||||
.single();
|
.single();
|
||||||
if (subscriptions) {
|
return subscriptions ? subscriptions : undefined;
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user