Skip to content

Commit 3d128dc

Browse files
authored
Merge pull request #1 from projets-fin-bac-24/feature/login-page
[PS-10] Créer une page de connexion WEB
2 parents 13ab459 + e534eb4 commit 3d128dc

File tree

12 files changed

+357
-174
lines changed

12 files changed

+357
-174
lines changed

.eslintrc.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module.exports = {
2+
"env": {
3+
"browser": true,
4+
"es2021": true,
5+
"node": true
6+
},
7+
"extends": [
8+
"eslint:recommended",
9+
"plugin:@typescript-eslint/recommended",
10+
"plugin:react/recommended"
11+
],
12+
"overrides": [
13+
{
14+
"env": {
15+
"node": true
16+
},
17+
"files": [
18+
".eslintrc.{js,cjs}"
19+
],
20+
"parserOptions": {
21+
"sourceType": "script"
22+
}
23+
}
24+
],
25+
"parser": "@typescript-eslint/parser",
26+
"parserOptions": {
27+
"ecmaVersion": "latest",
28+
"sourceType": "module"
29+
},
30+
"plugins": [
31+
"@typescript-eslint",
32+
"react"
33+
],
34+
"rules": {
35+
"react/react-in-jsx-scope": "off",
36+
}
37+
}

app/login/components/Checkbox.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface CheckboxProps {
2+
checked?: boolean;
3+
checkboxStyle?: string;
4+
textStyle?: string;
5+
text?: string;
6+
style?: string;
7+
}
8+
9+
export default function Checkbox(props: CheckboxProps) {
10+
return (
11+
<div className={`${props.style} flex flex-row`}>
12+
<input type="checkbox" defaultChecked={props.checked ?? false} className={`checkbox ${props.checkboxStyle}`} />
13+
<p className={`${props.textStyle} pl-2`}>{props.text}</p>
14+
</div>
15+
);
16+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use client';
2+
import { useState } from 'react';
3+
import Eye from '../../../public/Eye.svg';
4+
import Image from 'next/image';
5+
6+
export default function PasswordInput() {
7+
const [passwordShown, setPasswordShown] = useState(false);
8+
9+
const togglePasswordVisibility = () => {
10+
setPasswordShown(!passwordShown);
11+
};
12+
return (
13+
<div className='relative flex items-center justify-center border rounded-md'>
14+
<input
15+
className={`'text-xs rounded-md px-4 py-2 bg-inherit flex-1`}
16+
type={passwordShown ? 'text' : 'password'}
17+
name='password'
18+
required
19+
/>
20+
<button onClick={togglePasswordVisibility} className='absolute right-0 mr-3' type='button'>
21+
{passwordShown ? <Image src={Eye} alt={'eye'} /> : <Image src={Eye} alt={'eye'} />}
22+
</button>
23+
</div>
24+
);
25+
}

app/login/page.tsx

Lines changed: 62 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,65 @@
1-
import Link from "next/link";
2-
import { headers, cookies } from "next/headers";
3-
import { createClient } from "@/utils/supabase/server";
4-
import { redirect } from "next/navigation";
1+
import Checkbox from './components/Checkbox';
2+
import { signIn } from '@/utils/supabase/auth';
3+
import Link from 'next/link';
4+
import ETS from '../../public/ETS.svg';
5+
import PasswordInput from './components/PasswordInput';
6+
import Alert from '@/components/Alert';
7+
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
58

6-
export default function Login({
7-
searchParams,
8-
}: {
9-
searchParams: { message: string };
10-
}) {
11-
const signIn = async (formData: FormData) => {
12-
"use server";
9+
export default function Login({ searchParams }: { searchParams: { message: string; type: string } }) {
10+
return (
11+
<div className='animate-in relative flex justify-center items-center rounded-2xl w-screen h-screen'>
12+
<div className={`absolute bottom-4 right-4 w-[141px] h-[79px] bg-[url('../public/ETS.svg')] bg-cover`} />
13+
<div className='grid justify-items-center content-center bg-accent rounded-2xl min-h-min min-w-min '>
14+
<h1 className='py-10 text-4xl'>Bienvenue !</h1>
15+
<div className='flex-1 flex flex-col w-full px-8 sm:max-w-md justify-center gap-2'>
16+
<form className='flex-1 flex flex-col w-full justify-center gap-2 text-foreground' action={signIn}>
17+
{searchParams?.message && (
18+
<Alert
19+
customStyle={'flex flex-1 flex-col w-full pb-2 justify-center gap-2'}
20+
text={searchParams.message}
21+
alertType={searchParams.type}
22+
icon={faTriangleExclamation}
23+
/>
24+
)}
25+
<label className='text-md text-primary' htmlFor='email'>
26+
Courriel
27+
</label>
28+
<input className='rounded-md px-4 py-2 bg-inherit border mb-6' name='email' required />
29+
<label className='text-md text-primary' htmlFor='password'>
30+
Mot de passe
31+
</label>
32+
<PasswordInput />
33+
<Checkbox
34+
checked={true}
35+
style='self-end pb-6'
36+
text='Se souvenir de moi'
37+
textStyle='text-primary'
38+
checkboxStyle='checkbox-primary'
39+
/>
40+
<button className='btn btn-ghost bg-secondary rounded-md text-foreground text-base mb-2 hover:bg-secondary/75'>
41+
Se connecter
42+
</button>
43+
</form>
1344

14-
const email = formData.get("email") as string;
15-
const password = formData.get("password") as string;
16-
const cookieStore = cookies();
17-
const supabase = createClient(cookieStore);
18-
19-
const { error } = await supabase.auth.signInWithPassword({
20-
email,
21-
password,
22-
});
23-
24-
if (error) {
25-
return redirect("/login?message=Could not authenticate user");
26-
}
27-
28-
return redirect("/");
29-
};
30-
31-
const signUp = async (formData: FormData) => {
32-
"use server";
33-
34-
const origin = headers().get("origin");
35-
const email = formData.get("email") as string;
36-
const password = formData.get("password") as string;
37-
const cookieStore = cookies();
38-
const supabase = createClient(cookieStore);
39-
40-
const { error } = await supabase.auth.signUp({
41-
email,
42-
password,
43-
options: {
44-
emailRedirectTo: `${origin}/auth/callback`,
45-
},
46-
});
47-
48-
if (error) {
49-
return redirect("/login?message=Could not authenticate user");
50-
}
51-
52-
return redirect("/login?message=Check email to continue sign in process");
53-
};
54-
55-
return (
56-
<div className="flex-1 flex flex-col w-full px-8 sm:max-w-md justify-center gap-2">
57-
<Link
58-
href="/"
59-
className="absolute left-8 top-8 py-2 px-4 rounded-md no-underline text-foreground bg-btn-background hover:bg-btn-background-hover flex items-center group text-sm"
60-
>
61-
<svg
62-
xmlns="http://www.w3.org/2000/svg"
63-
width="24"
64-
height="24"
65-
viewBox="0 0 24 24"
66-
fill="none"
67-
stroke="currentColor"
68-
strokeWidth="2"
69-
strokeLinecap="round"
70-
strokeLinejoin="round"
71-
className="mr-2 h-4 w-4 transition-transform group-hover:-translate-x-1"
72-
>
73-
<polyline points="15 18 9 12 15 6" />
74-
</svg>{" "}
75-
Back
76-
</Link>
77-
78-
<form
79-
className="animate-in flex-1 flex flex-col w-full justify-center gap-2 text-foreground"
80-
action={signIn}
81-
>
82-
<label className="text-md" htmlFor="email">
83-
Email
84-
</label>
85-
<input
86-
className="rounded-md px-4 py-2 bg-inherit border mb-6"
87-
name="email"
88-
placeholder="[email protected]"
89-
required
90-
/>
91-
<label className="text-md" htmlFor="password">
92-
Password
93-
</label>
94-
<input
95-
className="rounded-md px-4 py-2 bg-inherit border mb-6"
96-
type="password"
97-
name="password"
98-
placeholder="••••••••"
99-
required
100-
/>
101-
<button className="bg-green-700 rounded-md px-4 py-2 text-foreground mb-2">
102-
Sign In
103-
</button>
104-
<button
105-
formAction={signUp}
106-
className="border border-foreground/20 rounded-md px-4 py-2 text-foreground mb-2"
107-
>
108-
Sign Up
109-
</button>
110-
{searchParams?.message && (
111-
<p className="mt-4 p-4 bg-foreground/10 text-foreground text-center">
112-
{searchParams.message}
113-
</p>
114-
)}
115-
</form>
116-
</div>
117-
);
45+
<div className='flex justify-center'>
46+
<p className='text-xs text-primary'>
47+
Vous avez oublié vos informations?
48+
<Link href={'/forgotPassword'} className='text-xs pl-1 underline text-secondary'>
49+
Réinitialisez votre mot de passe
50+
</Link>
51+
</p>
52+
</div>
53+
<div className='flex justify-center'>
54+
<p className='text-xs pb-10 text-primary'>
55+
Vous n&apos;avez pas de compte?
56+
<Link href={'/signUp'} className='text-xs pl-1 underline text-secondary'>
57+
Inscrivez-vous
58+
</Link>
59+
</p>
60+
</div>
61+
</div>
62+
</div>
63+
</div>
64+
);
11865
}

app/page.tsx

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,7 @@
1-
import DeployButton from "../components/DeployButton";
21
import AuthButton from "../components/AuthButton";
3-
import { createClient } from "@/utils/supabase/server";
4-
import ConnectSupabaseSteps from "@/components/ConnectSupabaseSteps";
5-
import SignUpUserSteps from "@/components/SignUpUserSteps";
6-
import Header from "@/components/Header";
7-
import { cookies } from "next/headers";
82

93
export default async function Index() {
10-
const cookieStore = cookies();
11-
12-
const canInitSupabaseClient = () => {
13-
// This function is just for the interactive tutorial.
14-
// Feel free to remove it once you have Supabase connected.
15-
try {
16-
createClient(cookieStore);
17-
return true;
18-
} catch (e) {
19-
return false;
20-
}
21-
};
22-
23-
const isSupabaseConnected = canInitSupabaseClient();
24-
254
return (
26-
<div className="flex-1 w-full flex flex-col gap-20 items-center">
27-
<nav className="w-full flex justify-center border-b border-b-foreground/10 h-16">
28-
<div className="w-full max-w-4xl flex justify-between items-center p-3 text-sm">
29-
<DeployButton />
30-
{isSupabaseConnected && <AuthButton />}
31-
</div>
32-
</nav>
33-
34-
<div className="animate-in flex-1 flex flex-col gap-20 opacity-0 max-w-4xl px-3">
35-
<Header />
36-
<main className="flex-1 flex flex-col gap-6">
37-
<h2 className="font-bold text-4xl mb-4">Next steps</h2>
38-
{isSupabaseConnected ? <SignUpUserSteps /> : <ConnectSupabaseSteps />}
39-
</main>
40-
</div>
41-
42-
<footer className="w-full border-t border-t-foreground/10 p-8 flex justify-center text-center text-xs">
43-
<p>
44-
Powered by{" "}
45-
<a
46-
href="https://supabase.com/?utm_source=create-next-app&utm_medium=template&utm_term=nextjs"
47-
target="_blank"
48-
className="font-bold hover:underline"
49-
rel="noreferrer"
50-
>
51-
Supabase
52-
</a>
53-
</p>
54-
</footer>
55-
</div>
5+
<AuthButton />
566
);
577
}

components/Alert.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
2+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3+
4+
interface AlertProps {
5+
text: string;
6+
alertType: string;
7+
customStyle?: string;
8+
textStyle?: string;
9+
icon: IconDefinition;
10+
}
11+
12+
export default function Alert(props: AlertProps) {
13+
const alertType = props.alertType ? `alert-${props.alertType}` : 'alert-error';
14+
return (
15+
<div className={props.customStyle}>
16+
<div role='alert' className={`alert ${alertType}`}>
17+
<FontAwesomeIcon icon={props.icon} className='w-5' />
18+
<p className={props.textStyle}>
19+
{props.text}
20+
</p>
21+
</div>
22+
</div>
23+
);
24+
}

0 commit comments

Comments
 (0)