mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-10 07:25:35 +00:00
Beaucoup trop de chose
- Ajout création, édition, suppression catégories & liens - Ajout auth google - Ajout/modification style pour catégories & liens - Ajout component générique pour bouton, inputs, checkbox & selector - Gestion des messages d'erreur/succès/infos via component dédié - Ajout component FormLayout pour les pages création, édition, suppression catégories & liens - Page custom 404, 500 & signin - Modification schéma DB
This commit is contained in:
14
pages/404.tsx
Normal file
14
pages/404.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import Head from 'next/head';
|
||||
import styles from '../styles/error-page.module.scss';
|
||||
|
||||
export default function Custom404() {
|
||||
return (<>
|
||||
<Head>
|
||||
<title>Superpipo — Page introuvable</title>
|
||||
</Head>
|
||||
<div className={styles['App']}>
|
||||
<h1>404</h1>
|
||||
<h2>Cette page est introuvable.</h2>
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
14
pages/500.tsx
Normal file
14
pages/500.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import Head from 'next/head';
|
||||
import styles from '../styles/error-page.module.scss';
|
||||
|
||||
export default function Custom500() {
|
||||
return (<>
|
||||
<Head>
|
||||
<title>Superpipo — Une erreur côté serveur est survenue</title>
|
||||
</Head>
|
||||
<div className={styles['App']}>
|
||||
<h1>500</h1>
|
||||
<h2>Une erreur côté serveur est survenue.</h2>
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { SessionProvider } from 'next-auth/react';
|
||||
import nProgress from "nprogress";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import nProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
import AuthRequired from '../components/AuthRequired';
|
||||
|
||||
import '../styles/globals.scss';
|
||||
import "nprogress/nprogress.css"
|
||||
import { useEffect } from 'react';
|
||||
|
||||
function MyApp({
|
||||
Component,
|
||||
@@ -26,7 +30,13 @@ function MyApp({
|
||||
|
||||
return (
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
{Component.authRequired ? (
|
||||
<AuthRequired>
|
||||
<Component {...pageProps} />
|
||||
</AuthRequired>
|
||||
) : (
|
||||
<Component {...pageProps} />
|
||||
)}
|
||||
</SessionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ import { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
const Document = () => (
|
||||
<Html lang='fr'>
|
||||
<Head />
|
||||
<Head>
|
||||
<link
|
||||
href='https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Rubik:ital,wght@0,400;0,700;1,400;1,700&display=swap'
|
||||
rel='stylesheet'
|
||||
/>
|
||||
</Head>
|
||||
<title>Superpipo</title>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import NextAuth from 'next-auth';
|
||||
import GoogleProvider from 'next-auth/providers/google';
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
authorization: {
|
||||
params: {
|
||||
prompt: "consent",
|
||||
access_type: "offline",
|
||||
response_type: "code"
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ account, profile }) {
|
||||
if (account.provider === "google" && profile.email !== '') {
|
||||
if (profile.email_verified && profile.email.endsWith("@gmail.com")) {
|
||||
return true;
|
||||
} else {
|
||||
return "/signin?error=" + encodeURI('Une erreur s\'est produite lors de l\'authentification');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
60
pages/api/auth/[...nextauth].ts
Normal file
60
pages/api/auth/[...nextauth].ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import NextAuth from 'next-auth';
|
||||
import GoogleProvider from 'next-auth/providers/google';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
authorization: {
|
||||
params: {
|
||||
prompt: 'consent',
|
||||
access_type: 'offline',
|
||||
response_type: 'code'
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ account: accountParam, profile }) { // TODO: Auth
|
||||
if (accountParam.provider !== 'google') {
|
||||
return '/signin?error=' + encodeURI('Authentitifcation via Google requise');
|
||||
}
|
||||
|
||||
const email = profile?.email;
|
||||
if (email === '') {
|
||||
return '/signin?error=' + encodeURI('Impossible de récupérer l\'email associé à ce compte Google');
|
||||
}
|
||||
|
||||
const googleId = profile?.sub;
|
||||
if (googleId === '') {
|
||||
return '/signin?error=' + encodeURI('Impossible de récupérer l\'identifiant associé à ce compte Google');
|
||||
}
|
||||
|
||||
try {
|
||||
const account = await prisma.user.findFirst({
|
||||
where: {
|
||||
google_id: googleId,
|
||||
email
|
||||
}
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return '/signin?error=' + encodeURI('Vous n\'êtes pas autorisé à vous connecter avec ce compte Google');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return '/signin?error=' + encodeURI('Une erreur est survenue lors de l\'authentification');
|
||||
}
|
||||
}
|
||||
},
|
||||
pages: {
|
||||
signIn: '/signin',
|
||||
error: '/signin'
|
||||
}
|
||||
});
|
||||
39
pages/api/category/create.ts
Normal file
39
pages/api/category/create.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.post(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const name = req.body?.name as string;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).send({ error: 'Nom de catégorie manquant' });
|
||||
}
|
||||
|
||||
try {
|
||||
const category = await prisma.category.findFirst({
|
||||
where: { name }
|
||||
});
|
||||
|
||||
if (category) {
|
||||
return res.status(400).send({ error: 'Une catégorie avec ce nom existe déjà' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la création de la catégorie' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.category.create({
|
||||
data: { name }
|
||||
});
|
||||
return res.status(200).send({ success: 'Catégorie créée avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la création de la catégorie' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
45
pages/api/category/edit/[cid].ts
Normal file
45
pages/api/category/edit/[cid].ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.put(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { cid } = req.query;
|
||||
|
||||
let category;
|
||||
try {
|
||||
category = await prisma.category.findFirst({
|
||||
where: { id: Number(cid) }
|
||||
});
|
||||
|
||||
if (!category) {
|
||||
return res.status(400).send({ error: 'Catégorie introuvable' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de l\'édition de la catégorie' });
|
||||
}
|
||||
|
||||
const name = req.body?.name as string;
|
||||
if (!name) {
|
||||
return res.status(400).send({ error: 'Nom de la catégorie manquante' });
|
||||
} else if (name === category.name) {
|
||||
return res.status(400).send({ error: 'Le nom de la catégorie doit être différent du nom actuel' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.category.update({
|
||||
where: { id: Number(cid) },
|
||||
data: { name }
|
||||
});
|
||||
|
||||
return res.status(200).send({ success: 'Catégorie mise à jour avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de l\'édition de la catégorie' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
36
pages/api/category/remove/[cid].ts
Normal file
36
pages/api/category/remove/[cid].ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.delete(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { cid } = req.query;
|
||||
|
||||
try {
|
||||
const category = await prisma.category.findFirst({
|
||||
where: { id: Number(cid) }
|
||||
});
|
||||
|
||||
if (!category) {
|
||||
return res.status(400).send({ error: 'Categorie introuvable' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la suppression de la catégorie' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.category.delete({
|
||||
where: { id: Number(cid) }
|
||||
});
|
||||
|
||||
return res.status(200).send({ success: 'La catégorie a été supprimée avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la suppression de la catégorie' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
68
pages/api/link/create.ts
Normal file
68
pages/api/link/create.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.post(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const name = req.body?.name as string;
|
||||
const url = req.body?.url as string;
|
||||
const favorite = Boolean(req.body?.favorite) || false;
|
||||
const categoryId = Number(req.body?.categoryId);
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).send({ error: 'Nom du lien manquant' });
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).send({ error: 'URL du lien manquant' });
|
||||
}
|
||||
|
||||
if (!categoryId) {
|
||||
return res.status(400).send({ error: 'Catégorie du lien manquante' });
|
||||
}
|
||||
|
||||
try {
|
||||
const link = await prisma.link.findFirst({
|
||||
where: { name }
|
||||
});
|
||||
|
||||
if (link) {
|
||||
return res.status(400).send({ error: 'Un lien avec ce nom existe déjà' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la création du lien' });
|
||||
}
|
||||
|
||||
try {
|
||||
const category = await prisma.category.findFirst({
|
||||
where: { id: categoryId }
|
||||
});
|
||||
|
||||
if (!category) {
|
||||
return res.status(400).send({ error: 'Cette catégorie n\'existe pas' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la création du lien' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.link.create({
|
||||
data: {
|
||||
name,
|
||||
url,
|
||||
categoryId,
|
||||
favorite
|
||||
}
|
||||
});
|
||||
return res.status(200).send({ success: 'Lien créé avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la création du lien' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
59
pages/api/link/edit/[lid].ts
Normal file
59
pages/api/link/edit/[lid].ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.put(async (req: NextApiRequest, res: NextApiResponse) => { // TODO: Ajouter vérification -> l'utilisateur doit changer au moins un champ
|
||||
const { lid } = req.query;
|
||||
|
||||
try {
|
||||
const link = await prisma.link.findFirst({
|
||||
where: { id: Number(lid) }
|
||||
});
|
||||
|
||||
if (!link) {
|
||||
return res.status(400).send({ error: 'Lien introuvable' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de l\'édition du lien' });
|
||||
}
|
||||
|
||||
const name = req.body?.name as string;
|
||||
const url = req.body?.url as string;
|
||||
const favorite = Boolean(req.body?.favorite) || false;
|
||||
const categoryId = Number(req.body?.categoryId);
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).send({ error: 'Nom du lien manquant' });
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).send({ error: 'URL du lien manquant' });
|
||||
}
|
||||
|
||||
if (!categoryId) {
|
||||
return res.status(400).send({ error: 'Catégorie du lien manquante' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.link.update({
|
||||
where: { id: Number(lid) },
|
||||
data: {
|
||||
name,
|
||||
url,
|
||||
favorite,
|
||||
categoryId
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(200).send({ success: 'Lien mis à jour avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de l\'édition du lien' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
36
pages/api/link/remove/[lid].ts
Normal file
36
pages/api/link/remove/[lid].ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { apiRoute } from '../../../../utils/back';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
apiRoute.delete(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { lid } = req.query;
|
||||
|
||||
try {
|
||||
const link = await prisma.link.findFirst({
|
||||
where: { id: Number(lid) }
|
||||
});
|
||||
|
||||
if (!link) {
|
||||
return res.status(400).send({ error: 'Lien introuvable' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la suppression du lien' });
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.link.delete({
|
||||
where: { id: Number(lid) }
|
||||
});
|
||||
|
||||
return res.status(200).send({ success: 'Le lien a été supprimé avec succès' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(400).send({ error: 'Une erreur est survenue lors de la suppression du lien' });
|
||||
}
|
||||
});
|
||||
|
||||
export default apiRoute;
|
||||
@@ -1,45 +1,65 @@
|
||||
import { useState } from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import nProgress from 'nprogress';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
import FormLayout from '../../components/FormLayout';
|
||||
import TextBox from '../../components/TextBox';
|
||||
|
||||
import Input from '../../components/input';
|
||||
import styles from '../../styles/create.module.scss';
|
||||
import Head from 'next/head';
|
||||
import { HandleAxiosError } from '../../utils/front';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function CreateCategory() {
|
||||
const { data: session, status } = useSession({ required: true });
|
||||
function CreateCategory() {
|
||||
const info = useRouter().query?.info as string;
|
||||
const [name, setName] = useState<string>('');
|
||||
|
||||
if (status === 'loading') {
|
||||
return (<p>Chargement de la session en cours</p>)
|
||||
}
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
useEffect(() => setCanSubmit(name.length !== 0), [name]);
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
console.log('On peut envoyer la requête pour créer une catégorie');
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const payload = { name };
|
||||
const { data }: AxiosResponse<any> = await axios.post('/api/category/create', payload);
|
||||
setSuccess(data?.success || 'Categorie créée avec succès');
|
||||
setCanSubmit(false);
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
setCanSubmit(true);
|
||||
} finally {
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`App ${styles['create-app']}`}>
|
||||
<Head>
|
||||
<title>Superpipo — Créer une categorie</title>
|
||||
</Head>
|
||||
<h2>Créer une categorie</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Input
|
||||
name='name'
|
||||
label='Nom de la catégorie'
|
||||
onChangeCallback={({ target }, value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
/>
|
||||
<button type='submit' disabled={name.length < 1}>
|
||||
Valider
|
||||
</button>
|
||||
</form>
|
||||
<Link href='/'>
|
||||
<a>← Revenir à l'accueil</a>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
return (<>
|
||||
<FormLayout
|
||||
title='Créer une catégorie'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
infoMessage={info}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom de la catégorie'
|
||||
onChangeCallback={(value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder='Nom...'
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
CreateCategory.authRequired = true;
|
||||
export default CreateCategory;
|
||||
|
||||
109
pages/category/edit/[cid].tsx
Normal file
109
pages/category/edit/[cid].tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import nProgress from 'nprogress';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import 'react-confirm-alert/src/react-confirm-alert.css';
|
||||
|
||||
import FormLayout from '../../../components/FormLayout';
|
||||
import TextBox from '../../../components/TextBox';
|
||||
|
||||
import styles from '../../../styles/create.module.scss';
|
||||
|
||||
import { Category } from '../../../types';
|
||||
import { BuildCategory, HandleAxiosError } from '../../../utils/front';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function EditCategory({ category }: { category: Category; }) {
|
||||
const [name, setName] = useState<string>(category.name);
|
||||
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (name !== category.name && name !== '') {
|
||||
setCanSubmit(true);
|
||||
} else {
|
||||
setCanSubmit(false);
|
||||
}
|
||||
}, [category, name]);
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
confirmAlert({
|
||||
message: `Confirmer l'édition de la catégorie "${category.name}"`,
|
||||
buttons: [{
|
||||
label: 'Yes',
|
||||
onClick: async () => {
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const payload = { name };
|
||||
const { data }: AxiosResponse<any> = await axios.put(`/api/category/edit/${category.id}`, payload);
|
||||
setSuccess(data?.success || 'Catégorie modifiée avec succès');
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
} finally {
|
||||
setCanSubmit(true);
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'No',
|
||||
onClick: () => { }
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
return (<>
|
||||
<FormLayout
|
||||
title='Modifier une catégorie'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom'
|
||||
onChangeCallback={(value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder={`Nom original : ${category.name}`}
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
EditCategory.authRequired = true;
|
||||
export default EditCategory;
|
||||
|
||||
export async function getServerSideProps({ query }) {
|
||||
const { cid } = query;
|
||||
const categoryDB = await prisma.category.findFirst({
|
||||
where: { id: Number(cid) },
|
||||
include: { links: true }
|
||||
});
|
||||
|
||||
if (!categoryDB) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const category = BuildCategory(categoryDB);
|
||||
return {
|
||||
props: {
|
||||
category: JSON.parse(JSON.stringify(category))
|
||||
}
|
||||
}
|
||||
}
|
||||
113
pages/category/remove/[cid].tsx
Normal file
113
pages/category/remove/[cid].tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import nProgress from 'nprogress';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import 'react-confirm-alert/src/react-confirm-alert.css';
|
||||
|
||||
import FormLayout from '../../../components/FormLayout';
|
||||
import TextBox from '../../../components/TextBox';
|
||||
|
||||
import styles from '../../../styles/create.module.scss';
|
||||
|
||||
import { Category } from '../../../types';
|
||||
import { BuildCategory, HandleAxiosError } from '../../../utils/front';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function RemoveCategory({ category }: { category: Category; }) {
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (category.links.length > 0) {
|
||||
setError('Vous devez supprimer tous les liens de cette catégorie avant de pouvoir supprimer cette catégorie')
|
||||
setCanSubmit(false);
|
||||
} else {
|
||||
setCanSubmit(true);
|
||||
}
|
||||
}, [category]);
|
||||
|
||||
if (status === 'loading') {
|
||||
return (<p>Chargement de la session en cours</p>)
|
||||
}
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
confirmAlert({
|
||||
message: `Confirmer la suppression du lien "${category.name}"`,
|
||||
buttons: [{
|
||||
label: 'Yes',
|
||||
onClick: async () => {
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const { data }: AxiosResponse<any> = await axios.delete(`/api/category/remove/${category.id}`);
|
||||
setSuccess(data?.success || 'Categorie supprimée avec succès');
|
||||
setCanSubmit(false);
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
setCanSubmit(true);
|
||||
} finally {
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'No',
|
||||
onClick: () => { }
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
return (<>
|
||||
<FormLayout
|
||||
title='Supprimer une catégorie'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
classBtnConfirm='red-btn'
|
||||
textBtnConfirm='Supprimer'
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom'
|
||||
value={category.name}
|
||||
fieldClass={styles['input-field']}
|
||||
disabled={true}
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
RemoveCategory.authRequired = true;
|
||||
export default RemoveCategory;
|
||||
|
||||
export async function getServerSideProps({ query }) {
|
||||
const { cid } = query;
|
||||
const categoryDB = await prisma.category.findFirst({
|
||||
where: { id: Number(cid) },
|
||||
include: { links: true }
|
||||
});
|
||||
|
||||
if (!categoryDB) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const category = BuildCategory(categoryDB);
|
||||
return {
|
||||
props: {
|
||||
category: JSON.parse(JSON.stringify(category))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import Head from 'next/head';
|
||||
|
||||
import Menu from '../components/Categories/SideMenu';
|
||||
@@ -10,7 +9,6 @@ import { Category, Link } from '../types';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
import { store } from '../redux';
|
||||
import { BuildCategory } from '../utils/front';
|
||||
|
||||
import Links from '../components/Links/Links';
|
||||
@@ -20,40 +18,35 @@ interface HomeProps {
|
||||
favorites: Link[];
|
||||
}
|
||||
|
||||
export default function Home({ categories, favorites }: HomeProps) {
|
||||
const { data: session, status } = useSession({ required: true });
|
||||
function Home({ categories, favorites }: HomeProps) {
|
||||
const { data } = useSession({ required: true });
|
||||
const [categoryActive, setCategoryActive] = useState<Category | null>(categories?.[0]);
|
||||
|
||||
const handleSelectCategory = (category: Category) => setCategoryActive(category);
|
||||
|
||||
if (status === 'loading') {
|
||||
return (<p>Chargement de la session en cours</p>)
|
||||
}
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Head>
|
||||
<title>Superpipo</title>
|
||||
</Head>
|
||||
<div className='App'>
|
||||
<Menu
|
||||
categories={categories}
|
||||
favorites={favorites}
|
||||
handleSelectCategory={handleSelectCategory}
|
||||
categoryActive={categoryActive}
|
||||
session={session}
|
||||
/>
|
||||
<Links category={categoryActive} />
|
||||
</div>
|
||||
</Provider>
|
||||
)
|
||||
return (<>
|
||||
<Head>
|
||||
<title>Superpipo</title>
|
||||
</Head>
|
||||
<div className='App'>
|
||||
<Menu
|
||||
categories={categories}
|
||||
favorites={favorites}
|
||||
handleSelectCategory={handleSelectCategory}
|
||||
categoryActive={categoryActive}
|
||||
session={data}
|
||||
/>
|
||||
<Links category={categoryActive} />
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
export async function getServerSideProps() {
|
||||
const categoriesDB = await prisma.category.findMany({ include: { links: true } });
|
||||
|
||||
const favorites = [] as Link[];
|
||||
const categories = categoriesDB.map((categoryDB) => {
|
||||
console.log(categoryDB)
|
||||
const category = BuildCategory(categoryDB);
|
||||
category.links.map((link) => link.favorite ? favorites.push(link) : null);
|
||||
return category;
|
||||
@@ -62,7 +55,7 @@ export async function getStaticProps() {
|
||||
if (categories.length === 0) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/category/create'
|
||||
destination: '/category/create?info=Veuillez créer une catégorie'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,4 +66,7 @@ export async function getStaticProps() {
|
||||
favorites: JSON.parse(JSON.stringify(favorites)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Home.authRequired = true;
|
||||
export default Home;
|
||||
@@ -1,88 +1,103 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import Input from '../../components/input';
|
||||
import nProgress from 'nprogress';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
import FormLayout from '../../components/FormLayout';
|
||||
import TextBox from '../../components/TextBox';
|
||||
import Selector from '../../components/Selector';
|
||||
import Checkbox from '../../components/Checkbox';
|
||||
|
||||
import styles from '../../styles/create.module.scss';
|
||||
|
||||
import { Category } from '../../types';
|
||||
import { BuildCategory } from '../../utils/front';
|
||||
import { BuildCategory, HandleAxiosError, IsValidURL } from '../../utils/front';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import Selector from '../../components/selector';
|
||||
import Head from 'next/head';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default function CreateLink({ categories }: { categories: Category[]; }) {
|
||||
const { status } = useSession({ required: true });
|
||||
function CreateLink({ categories }: { categories: Category[]; }) {
|
||||
const [name, setName] = useState<string>('');
|
||||
const [url, setUrl] = useState<string>('');
|
||||
const [favorite, setFavorite] = useState<boolean>(false);
|
||||
const [categoryId, setCategoryId] = useState<number | null>(categories?.[0].id || null);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const regex = new RegExp('https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,}');
|
||||
if (name !== '' && url.match(regex) && categoryId !== null) {
|
||||
setCanSubmit(false);
|
||||
} else {
|
||||
if (name !== '' && IsValidURL(url) && favorite !== null && categoryId !== null) {
|
||||
setCanSubmit(true);
|
||||
} else {
|
||||
setCanSubmit(false);
|
||||
}
|
||||
}, [name, url, categoryId]);
|
||||
}, [name, url, favorite, categoryId]);
|
||||
|
||||
if (status === 'loading') {
|
||||
return (<p>Chargement de la session en cours</p>)
|
||||
}
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
console.log('On peut envoyer la requête pour créer un lien');
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const payload = { name, url, favorite, categoryId };
|
||||
const { data }: AxiosResponse<any> = await axios.post('/api/link/create', payload);
|
||||
setSuccess(data?.success || 'Lien modifié avec succès');
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
} finally {
|
||||
setCanSubmit(true);
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Head>
|
||||
<title>Superpipo — Créer un lien</title>
|
||||
</Head>
|
||||
<div className={`App ${styles['create-app']}`}>
|
||||
<h2>Créer un lien</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Input
|
||||
name='name'
|
||||
label='Label'
|
||||
onChangeCallback={({ target }, value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder='Label du lien'
|
||||
/>
|
||||
<Input
|
||||
name='url'
|
||||
label='URL'
|
||||
onChangeCallback={({ target }, value) => setUrl(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder='URL du lien'
|
||||
/>
|
||||
<Selector
|
||||
name='category'
|
||||
label='Catégorie'
|
||||
value={categoryId}
|
||||
onChangeCallback={({ target }, value) => setCategoryId(value)}
|
||||
>
|
||||
{categories.map((category, key) => (
|
||||
<option key={key} value={category.id}>{category.name}</option>
|
||||
))}
|
||||
</Selector>
|
||||
<button type='submit' disabled={canSubmit}>
|
||||
Valider
|
||||
</button>
|
||||
</form>
|
||||
<Link href='/'>
|
||||
<a>← Revenir à l'accueil</a>
|
||||
</Link>
|
||||
</div>
|
||||
<FormLayout
|
||||
title='Créer un lien'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom'
|
||||
onChangeCallback={(value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder='Nom du lien'
|
||||
/>
|
||||
<TextBox
|
||||
name='url'
|
||||
label='URL'
|
||||
onChangeCallback={(value) => setUrl(value)}
|
||||
value={url}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder='https://www.example.org/'
|
||||
/>
|
||||
<Selector
|
||||
name='category'
|
||||
label='Catégorie'
|
||||
value={categoryId}
|
||||
onChangeCallback={(value: number) => setCategoryId(value)}
|
||||
options={categories.map(({ id, name }) => ({ label: name, value: id }))}
|
||||
/>
|
||||
<Checkbox
|
||||
name='favorite'
|
||||
isChecked={favorite}
|
||||
onChangeCallback={(value) => setFavorite(value)}
|
||||
label='Favoris'
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
CreateLink.authRequired = true;
|
||||
export default CreateLink;
|
||||
|
||||
export async function getServerSideProps() {
|
||||
const categoriesDB = await prisma.category.findMany();
|
||||
const categories = categoriesDB.map((categoryDB) => BuildCategory(categoryDB));
|
||||
|
||||
|
||||
130
pages/link/edit/[lid].tsx
Normal file
130
pages/link/edit/[lid].tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import nProgress from 'nprogress';
|
||||
|
||||
import FormLayout from '../../../components/FormLayout';
|
||||
import TextBox from '../../../components/TextBox';
|
||||
import Selector from '../../../components/Selector';
|
||||
import Checkbox from '../../../components/Checkbox';
|
||||
|
||||
import styles from '../../../styles/create.module.scss';
|
||||
|
||||
import { Category, Link } from '../../../types';
|
||||
import { BuildCategory, BuildLink, HandleAxiosError, IsValidURL } from '../../../utils/front';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function EditLink({ link, categories }: { link: Link; categories: Category[]; }) {
|
||||
const [name, setName] = useState<string>(link.name);
|
||||
const [url, setUrl] = useState<string>(link.url);
|
||||
const [favorite, setFavorite] = useState<boolean>(link.favorite);
|
||||
const [categoryId, setCategoryId] = useState<number | null>(link.category?.id || null);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (name !== link.name || url !== link.url || favorite !== link.favorite || categoryId !== link.category.id) {
|
||||
if (name !== '' && IsValidURL(url) && favorite !== null && categoryId !== null) {
|
||||
setCanSubmit(true);
|
||||
} else {
|
||||
setCanSubmit(false);
|
||||
}
|
||||
} else {
|
||||
setCanSubmit(false);
|
||||
}
|
||||
}, [name, url, favorite, categoryId, link]);
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const payload = { name, url, favorite, categoryId };
|
||||
const { data }: AxiosResponse<any> = await axios.put(`/api/link/edit/${link.id}`, payload);
|
||||
setSuccess(data?.success || 'Lien modifié avec succès');
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
} finally {
|
||||
setCanSubmit(true);
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
|
||||
return (<>
|
||||
<FormLayout
|
||||
title='Modifier un lien'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom'
|
||||
onChangeCallback={(value) => setName(value)}
|
||||
value={name}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder={`Nom original : ${link.name}`}
|
||||
/>
|
||||
<TextBox
|
||||
name='url'
|
||||
label='URL'
|
||||
onChangeCallback={(value) => setUrl(value)}
|
||||
value={url}
|
||||
fieldClass={styles['input-field']}
|
||||
placeholder={`URL original : ${link.url}`}
|
||||
/>
|
||||
<Selector
|
||||
name='category'
|
||||
label='Catégorie'
|
||||
value={categoryId}
|
||||
onChangeCallback={(value: number) => setCategoryId(value)}
|
||||
options={categories.map(({ id, name }) => ({ label: name, value: id }))}
|
||||
/>
|
||||
<Checkbox
|
||||
name='favorite'
|
||||
isChecked={favorite}
|
||||
onChangeCallback={(value) => setFavorite(value)}
|
||||
label='Favoris'
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
EditLink.authRequired = true;
|
||||
export default EditLink;
|
||||
|
||||
export async function getServerSideProps({ query }) {
|
||||
const { lid } = query;
|
||||
|
||||
const categoriesDB = await prisma.category.findMany();
|
||||
const categories = categoriesDB.map((categoryDB) => BuildCategory(categoryDB));
|
||||
|
||||
const linkDB = await prisma.link.findFirst({
|
||||
where: { id: Number(lid) },
|
||||
include: { category: true }
|
||||
});
|
||||
|
||||
if (!linkDB) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const link = BuildLink(linkDB, { categoryId: linkDB.categoryId, categoryName: linkDB.category.name });
|
||||
return {
|
||||
props: {
|
||||
link: JSON.parse(JSON.stringify(link)),
|
||||
categories: JSON.parse(JSON.stringify(categories))
|
||||
}
|
||||
}
|
||||
}
|
||||
122
pages/link/remove/[lid].tsx
Normal file
122
pages/link/remove/[lid].tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import nProgress from 'nprogress';
|
||||
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import 'react-confirm-alert/src/react-confirm-alert.css';
|
||||
|
||||
import FormLayout from '../../../components/FormLayout';
|
||||
import TextBox from '../../../components/TextBox';
|
||||
import Checkbox from '../../../components/Checkbox';
|
||||
|
||||
import styles from '../../../styles/create.module.scss';
|
||||
|
||||
import { Link } from '../../../types';
|
||||
import { BuildLink, HandleAxiosError } from '../../../utils/front';
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function RemoveLink({ link }: { link: Link; }) {
|
||||
|
||||
const [canSubmit, setCanSubmit] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
confirmAlert({
|
||||
message: `Confirmer la suppression du lien "${link.name}"`,
|
||||
buttons: [{
|
||||
label: 'Confirmer',
|
||||
onClick: async () => {
|
||||
setSuccess(null);
|
||||
setError(null);
|
||||
setCanSubmit(false);
|
||||
nProgress.start();
|
||||
|
||||
try {
|
||||
const { data }: AxiosResponse<any> = await axios.delete(`/api/link/remove/${link.id}`);
|
||||
setSuccess(data?.success || 'Lien supprimé avec succès');
|
||||
setCanSubmit(false);
|
||||
} catch (error) {
|
||||
setError(HandleAxiosError(error));
|
||||
setCanSubmit(true);
|
||||
} finally {
|
||||
nProgress.done();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Annuler',
|
||||
onClick: () => { }
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
return (<>
|
||||
<FormLayout
|
||||
title='Supprimer un lien'
|
||||
errorMessage={error}
|
||||
successMessage={success}
|
||||
canSubmit={canSubmit}
|
||||
handleSubmit={handleSubmit}
|
||||
classBtnConfirm='red-btn'
|
||||
textBtnConfirm='Supprimer'
|
||||
>
|
||||
<TextBox
|
||||
name='name'
|
||||
label='Nom'
|
||||
value={link.name}
|
||||
fieldClass={styles['input-field']}
|
||||
disabled={true}
|
||||
/>
|
||||
<TextBox
|
||||
name='url'
|
||||
label='URL'
|
||||
value={link.url}
|
||||
fieldClass={styles['input-field']}
|
||||
disabled={true}
|
||||
/>
|
||||
<TextBox
|
||||
name='category'
|
||||
label='Catégorie'
|
||||
value={link.category.name}
|
||||
fieldClass={styles['input-field']}
|
||||
disabled={true}
|
||||
/>
|
||||
<Checkbox
|
||||
name='favorite'
|
||||
label='Favoris'
|
||||
isChecked={link.favorite}
|
||||
disabled={true}
|
||||
/>
|
||||
</FormLayout>
|
||||
</>);
|
||||
}
|
||||
|
||||
RemoveLink.authRequired = true;
|
||||
export default RemoveLink;
|
||||
|
||||
export async function getServerSideProps({ query }) {
|
||||
const { lid } = query;
|
||||
const linkDB = await prisma.link.findFirst({
|
||||
where: { id: Number(lid) },
|
||||
include: { category: true }
|
||||
});
|
||||
|
||||
if (!linkDB) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const link = BuildLink(linkDB, { categoryId: linkDB.categoryId, categoryName: linkDB.category.name });
|
||||
return {
|
||||
props: {
|
||||
link: JSON.parse(JSON.stringify(link))
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pages/signin.tsx
Normal file
55
pages/signin.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { getProviders, signIn, useSession } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Head from 'next/head';
|
||||
|
||||
import styles from '../styles/login.module.scss';
|
||||
import MessageManager from '../components/MessageManager';
|
||||
|
||||
export default function SignIn({ providers }) {
|
||||
const { data: session, status } = useSession();
|
||||
const info = useRouter().query?.info as string;
|
||||
const error = useRouter().query?.error as string;
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className='App' style={{ alignItems: 'center' }}>
|
||||
<p style={{ height: 'fit-content' }}>Chargement de la session en cours</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Head>
|
||||
<title>Superpipo — Authentification</title>
|
||||
</Head>
|
||||
<div className='App'>
|
||||
<div className={styles['wrapper']}>
|
||||
<h2>Se connecter</h2>
|
||||
<MessageManager
|
||||
error={error}
|
||||
info={info}
|
||||
/>
|
||||
{session !== null && (<MessageManager info='Vous êtes déjà connecté' />)}
|
||||
<div className={styles['providers']}>
|
||||
{Object.values(providers).map(({ name, id }) => (
|
||||
<button key={id} onClick={() => signIn(id, { callbackUrl: '/' })} disabled={session !== null}>
|
||||
Continuer avec {name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<Link href='/'>
|
||||
<a>← Revenir à l'accueil</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const providers = await getProviders();
|
||||
return {
|
||||
props: { providers }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user