Skip to content

Commit

Permalink
Merge pull request #12 from ifspcbt-devspace/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
oproprioleonardo authored Sep 24, 2024
2 parents 8b7bfff + 4a693fd commit 6abd895
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 561 deletions.
79 changes: 67 additions & 12 deletions src/app/auth/log-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import Image from "next/image";

import "./login.css";
import {Button, Input} from "@nextui-org/react";
import React, {Suspense, useState} from "react";
import {useSearchParams} from "next/navigation";
import React, {FormEvent, Suspense, useEffect, useMemo, useState} from "react";
import {useRouter, useSearchParams} from "next/navigation";
import Loading from "@/app/auth/email/confirmation/[token]/loading";
import {isEmail} from "@/utils";
import {isEmail, toastConfig} from "@/utils";
import {toast} from "react-toastify";
import {isAuthenticated, login} from "@/server-actions/auth.action";

export default function LoginPage() {
return (
Expand All @@ -19,17 +21,69 @@ export default function LoginPage() {
}

function Login() {
const [isLoading, setIsLoading] = React.useState(false);
const [email, setEmail] = React.useState("");
const [isLoading, setIsLoading] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [sessionVerified, setSessionVerified] = useState(false);
const router = useRouter();
const params = useSearchParams();
const redirectTo = params.get("redir") || "/";

const isEmailInvalid = React.useMemo(() => {
useEffect(() => {
const load = async () => {
const isAuth = await isAuthenticated();
if (isAuth) {
router.replace(redirectTo);
} else setSessionVerified(true);
}

load();
}, [router]);

const validatePassword = (value: string) => {
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{6,}$/;
return passwordRegex.test(value);
};

const isEmailInvalid = useMemo(() => {
if (email === "") return false;
return !isEmail(email.trim());
}, [email]);

const isPasswordInvalid = useMemo(() => {
if (password === "") return false;
return !validatePassword(password);
}, [password]);

const handleOnSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true)
if (email === "" || password === "") {
toast.error("Preencha todos os campos", toastConfig)
setIsLoading(false);
return;
}
if (isEmailInvalid || isPasswordInvalid) {
toast.error("Corrija as credenciais primeiro", toastConfig);
setIsLoading(false);
return;
}
const formData = new FormData();
formData.append("email", email);
formData.append("password", password);
const respLogin = await login(formData);
if (respLogin && respLogin.error) {
setError(respLogin.error);
} else {
toast.success("Conta criada com sucesso, redirecionando...", toastConfig);
setTimeout(() => router.replace(redirectTo), 1500)
}
setIsLoading(false)
}

if (!sessionVerified) return <></>

return (
<div className="full-page-wrapper text-black">
<title>Login | IFSP Eventos</title>
Expand All @@ -40,21 +94,22 @@ function Login() {
</Link>

<div className="p-10 bg-white rounded-xl shadow-sm max-w-[380px]">
<form className="block mt-0">
<form className="block mt-0" onSubmit={handleOnSubmit}>
<div className="mb-8 text-center min-w-72">
<span className="font-semibold block mb-3 text-2xl">Entrar</span>
<p className="mb-1.5 text-sm opacity-75">Preencha seus dados de login abaixo.</p>
</div>
<Input maxLength={128} placeholder="E-mail" name="email" title="E-mail"
isInvalid={isEmailInvalid} errorMessage="O e-mail é inválido" onValueChange={setEmail}
classNames={{inputWrapper: "rounded-[9px]", base: "mb-1"}} type="email" autoComplete="off"
classNames={{inputWrapper: "rounded-[9px]", base: "mb-1"}} type="email" autoComplete="email"
isRequired={true}/>

<Input maxLength={16} placeholder="Senha" name="password" title="Senha" onValueChange={setPassword}
classNames={{inputWrapper: "rounded-[9px]", base: "mb-1"}} type="password" autoComplete="off"
isInvalid={isPasswordInvalid} errorMessage=""
classNames={{inputWrapper: "rounded-[9px]", base: "mb-1"}} type="password" autoComplete="password"
isRequired={true}/>

<Button isLoading={false} type="submit" className="mt-4 button data-[hover=true]:opacity-100">
<Button isLoading={isLoading} type="submit" className="mt-4 button data-[hover=true]:opacity-100">
{!isLoading && "Entrar"}
</Button>
<div className="form-card-footer">
Expand All @@ -65,8 +120,8 @@ function Login() {
</form>
</div>

<div className="hidden bg-silver text-small p-4 text-black rounded-md mt-2.5 max-w-[380px]">
{/*show error*/}
<div className={`${error ? "" : "hidden "} bg-silver text-small p-4 text-black rounded-md mt-2.5 max-w-[380px]`}>
{error}
</div>

<Link href={`/auth/reset-password${redirectTo === "/" ? "" : `?redir=${redirectTo}`}`}
Expand Down
18 changes: 16 additions & 2 deletions src/app/auth/sign-up/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import Image from "next/image";
import "./signup.css";
import {Button, Input, useDisclosure} from "@nextui-org/react";
import {useMask} from "@react-input/mask";
import React, {FormEvent, Suspense, useMemo, useState} from "react";
import React, {FormEvent, Suspense, useEffect, useMemo, useState} from "react";
import {useRouter, useSearchParams} from "next/navigation";
import Loading from "@/app/auth/email/confirmation/[token]/loading";
import {generateRandomUsername, isCPF, isEmail, isNumberPhone, isValidBirthDate, toastConfig} from "@/utils";
import {toast} from "react-toastify";
import {login, register} from "@/server-actions/auth.action";
import {isAuthenticated, login, register} from "@/server-actions/auth.action";
import ConfirmRegister from "@/components/register/ConfirmRegister";

export default function RegisterPage() {
Expand All @@ -32,11 +32,23 @@ function Register() {
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [sessionVerified, setSessionVerified] = useState(false);
const {isOpen, onOpen, onOpenChange} = useDisclosure();
const router = useRouter();
const params = useSearchParams();
const redirectTo = params.get("redir") || "/";

useEffect(() => {
const load = async () => {
const isAuth = await isAuthenticated();
if (isAuth) {
router.replace(redirectTo);
} else setSessionVerified(true);
}

load();
}, [router]);

const phoneRef = useMask({
mask: "(__) _____-____",
replacement: {_: /\d/},
Expand Down Expand Up @@ -126,6 +138,8 @@ function Register() {
onOpen();
}

if (!sessionVerified) return <></>

return (
<div className="full-page-wrapper text-black">
<title>Registrar | IFSP Eventos</title>
Expand Down
51 changes: 27 additions & 24 deletions src/app/events/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import Header from "@/components/Header";
import React from "react";
import React, {Suspense} from "react";
import Footer from "@/components/Footer";
import DarkPageHeader from "@/components/DarkPageHeader";
import "@/components/darkpageheader.css";
import Link from "next/link";
import {getEvent} from "@/server-actions/event.action";
import {Metadata} from "next";
import EventView from "@/components/events/single/EventView";

export default function EventPage({params}: { params: { id: string } }) {
const name = "Hallowif";
export async function generateMetadata(
{params}: { params: { id: string } }
): Promise<Metadata> {
// read route params
const id = params.id

const event = await getEvent(id);
if ("error" in event) {
return {
title: "Evento não encontrado",
description: "O evento que você está tentando acessar não foi encontrado.",
}
}

return {
title: event.name,
description: event.description,
}
}

export default function EventPage({params}: { params: { id: string } }) {
return (
<div className="bg-back-grey">
<title>Hallowif | IFSP Eventos</title>
<div className="min-h-[90vh] bg-white">
<Header/>
<DarkPageHeader title={"Hallowif - 08/10"} subtitle={"Por IFSP Cubatão"}/>
<div className="py-10 grid grid-cols-10 w-full">
<div className={"col-start-3 col-span-6"}>
<div className="event-page-grid">
<div className="font-semibold relative">
<p className="text-lg mb-8">Repudiandae asperiores excepturi repellat minus id et.
Saepe molestiae accusantium fugit et aut.
Reicie</p>
<Link href={"/auth/sign-up"}>
<div
className={`inline-block cursor-pointer duration-200 bg-neutral-900 hover:bg-opacity-90 text-white py-2 px-7 rounded-md`}>
Inscreva-se
</div>
</Link>
</div>
</div>
</div>
</div>
<Suspense fallback={"Carregando..."}>
<EventView params={params}/>
</Suspense>
</div>
<Footer/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/DarkPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function DarkPageHeader({title, subtitle}: { title: string, subti
<h1 className={"mb-4 text-5xl font-semibold block"}>{title}</h1>
<p className="text-xl">{subtitle}</p>
</div>
<div className="event-header-block">
<div className="event-header-block z-10">
<Image src={"/images/default-event-thumb.svg"} alt={"Event thumbnail"} width={650} height={100} className="event-cover"/>
</div>

Expand Down
58 changes: 47 additions & 11 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
"use client"

import Image from "next/image";
import {CiSearch} from "react-icons/ci";
import Link from "next/link";
import {redirectSearch} from "@/server-actions/redirect-search.action";
import {useEffect, useState} from "react";
import {isAuthenticated, logout} from "@/server-actions/auth.action";
import {toastConfig} from "@/utils";
import {toast} from "react-toastify";

export default function Header({search = ""}: { search?: string }) {
const [isLogged, setIsLogged] = useState(false);

useEffect(() => {
Promise.all([isAuthenticated()]).then(([isAuth]) => {
setIsLogged(isAuth)
});
}, [setIsLogged]);

const handleLogout = async () => {
await logout();
}

export default function Header({search=""} : {search?: string}) {
return (<>
<div className={`w-full grid grid-cols-10 bg-white text-black sticky top-0 z-50`}>

Expand All @@ -24,7 +42,8 @@ export default function Header({search=""} : {search?: string}) {
<form action={redirectSearch}
className={`ml-6 my-auto bg-neutral-100 py-1.5 px-2 rounded-md border-1 w-72 transition-colors duration-200 border-neutral-100 hover:border-neutral-300 focus-within:border-neutral-300 flex items-center`}>
<CiSearch className={"text-neutral-500 font-bold text-xl"}/>
<input type="text" defaultValue={search} name="search" maxLength={255} autoComplete="off" title={`Digite o nome do evento`}
<input type="text" defaultValue={search} name="search" maxLength={255} autoComplete="off"
title={`Digite o nome do evento`}
className={`ml-1.5 bg-transparent outline-none w-full text-sm font-medium appearance-none`}
placeholder={"Pesquisar eventos"}/>
<input type="submit" className="hidden" value="Search"/>
Expand All @@ -36,15 +55,32 @@ export default function Header({search=""} : {search?: string}) {
<div className={`inline-block cursor-pointer text-neutral-500 duration-200 hover:text-neutral-950`}>
<Link href={"/events"}>Eventos</Link>
</div>
<div className={`inline-block cursor-pointer text-neutral-500 duration-200 hover:text-neutral-950`}>
<Link href={"/auth/log-in"}>Entrar</Link>
</div>
<Link href={"/auth/sign-up"}>
<div
className={`inline-block cursor-pointer duration-200 bg-neutral-900 hover:bg-opacity-90 text-white py-1.5 px-5 rounded-md`}>
Registre-se
</div>
</Link>
{isLogged ? (
<>
<div className={`inline-block cursor-pointer text-neutral-500 duration-200 hover:text-neutral-950`}>
<Link href={"#"} onClick={async (e) => {
e.preventDefault();
toast.warn("Em breve", toastConfig);
}}>Conta</Link>
</div>
<div className={`inline-block cursor-pointer text-neutral-500 duration-200 hover:text-neutral-950`}>
<Link href={"/auth/log-in"} onClick={handleLogout}>Sair</Link>
</div>
</>
) : (
<>
<div className={`inline-block cursor-pointer text-neutral-500 duration-200 hover:text-neutral-950`}>
<Link href={"/auth/log-in"}>Entrar</Link>
</div>
<Link href={"/auth/sign-up"}>
<div
className={`inline-block cursor-pointer duration-200 bg-neutral-900 hover:bg-opacity-90 text-white py-1.5 px-5 rounded-md`}>
Registre-se
</div>
</Link>
</>
)}

</nav>
</div>

Expand Down
12 changes: 6 additions & 6 deletions src/components/events/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ export default function ({
id: string;
name: string;
description: string;
img: string;
imgUrl: string;
owner: string;
date: string;
date: Date;
}
}) {

return (
<Link href={"#"}
<Link href={"/events/" + event.id}
className="flex flex-col bg-transparent rounded-2xl col-span-1 row-span-1 h-full w-full duration-200 cursor-pointer no-underline hover:-translate-y-2">
<div className="relative w-full h-full rounded-xl mb-4 overflow-hidden">
<Image
src={event.img}
src={event.imgUrl}
alt="event-image"
quality={100}
height={100}
Expand All @@ -31,8 +31,8 @@ export default function ({
<div className={"text-sm opacity-75 mb-1.5"}>Por {event.owner}</div>
<div className={`text-black text-left max-w-[450px]`}>
<span
className={"text-black mb-3 text-2xl block font-semibold"}>{event.name + " - " + new Date(event.date).toLocaleString([], {dateStyle: "short"})}</span>
<p className={"text-sm opacity-75"}>{event.description}</p>
className={"text-black mb-3 text-2xl block font-semibold"}>{event.name + " - " + event.date.toLocaleString([], {dateStyle: "short"})}</span>
<p className={"text-sm opacity-75 line-clamp-5"}>{event.description}</p>
</div>
</Link>
);
Expand Down
Loading

0 comments on commit 6abd895

Please sign in to comment.