Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
STRAPI_URL=http://localhost:1337/api
STRAPI_URL=http://localhost:1337
NEXTAUTH_SECRET=<SECRET>
NEXTAUTH_URL=http://localhost:3000
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ And that's it! Your `SkateHub Frontend` should now be up and running locally on

### 2024

- 2024-03-26 - Create the header component [#5](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/5) _(v0.1.4)_
- 2024-03-26 - Create the home page of the SkateHub project [#4](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/4) _(v0.1.3)_
- 2024-03-26 - Create the sidebar provider and components to handle with the aside menu [#3](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/3) _(v0.1.2)_
- 2024-03-27 - Create the `signin` page of the SkateHub project [#7](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/7) _(v0.1.5)_
- 2024-03-26 - Create the `header` component [#5](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/5) _(v0.1.4)_
- 2024-03-26 - Create the `home` page of the SkateHub project [#4](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/4) _(v0.1.3)_
- 2024-03-26 - Create the `sidebar` provider and components to handle with the aside menu [#3](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/3) _(v0.1.2)_
- 2024-03-24 - Add [Chakra UI](https://chakra-ui.com/) to handle with the user interface [#2](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/2) _(v0.1.1)_
- 2024-03-19 - Add a quick guide to getting started with the application [#1](https://github.com/jpcmf/Frontend-GraduateProgram-FullStack-2024/pull/1) _(v0.1.0)_

Expand Down
6,099 changes: 1,400 additions & 4,699 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frontend",
"version": "0.1.4",
"version": "0.1.5",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -13,12 +13,16 @@
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.4",
"axios": "^1.6.8",
"framer-motion": "^11.0.20",
"next": "latest",
"next-auth": "^4.24.7",
"react": "latest",
"react-dom": "latest",
"react-icons": "^5.0.1"
"react-hook-form": "^7.51.2",
"react-icons": "^5.0.1",
"yup": "^1.4.0"
},
"devDependencies": {
"@types/node": "latest",
Expand Down
44 changes: 44 additions & 0 deletions src/components/Form/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FieldError } from "react-hook-form";
import { forwardRef, ForwardRefRenderFunction } from "react";
import {
Input as ChakraInput,
FormLabel,
FormControl,
InputProps as ChakraInputProps,
FormErrorMessage
} from "@chakra-ui/react";

interface InputProps extends ChakraInputProps {
label?: string;
error?: FieldError;
}

const InputBase: ForwardRefRenderFunction<HTMLInputElement, InputProps> = (
{ name, label, error = null, ...rest },
ref
) => {
return (
<FormControl isInvalid={!!error}>
{!!label && <FormLabel htmlFor={name}>{label}</FormLabel>}
<ChakraInput
name={name}
id={name}
focusBorderColor="blue.500"
bgColor="gray.900"
variant="filled"
_hover={{ bgColor: "gray.900" }}
size="lg"
ref={ref}
{...rest}
/>

{!!error && (
<FormErrorMessage as="small" color="red.500" ml="1" mt="2" display="block" fontSize="smaller" fontWeight="300">
{error?.message}
</FormErrorMessage>
)}
</FormControl>
);
};

export const Input = forwardRef(InputBase);
30 changes: 17 additions & 13 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { theme } from '../styles/theme'
import { fonts } from '@/lib/fonts'
import { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'
import { SidebarDrawerProvider } from '@/contexts/SidebarDrawerContext'
import { theme } from "../styles/theme";
import { fonts } from "@/lib/fonts";
import { Session } from "next-auth";
import { AppProps } from "next/app";
import { ChakraProvider } from "@chakra-ui/react";
import { SessionProvider } from "next-auth/react";
import { SidebarDrawerProvider } from "@/contexts/SidebarDrawerContext";

function MyApp({ Component, pageProps }: AppProps) {
function MyApp({ Component, pageProps }: AppProps<{ session: Session }>) {
return (
<>
<style jsx global>
Expand All @@ -14,13 +16,15 @@ function MyApp({ Component, pageProps }: AppProps) {
}
`}
</style>
<ChakraProvider theme={theme}>
<SidebarDrawerProvider>
<Component {...pageProps} />
</SidebarDrawerProvider>
</ChakraProvider>
<SessionProvider session={pageProps.session}>
<ChakraProvider theme={theme}>
<SidebarDrawerProvider>
<Component {...pageProps} />
</SidebarDrawerProvider>
</ChakraProvider>
</SessionProvider>
</>
)
);
}

export default MyApp
export default MyApp;
54 changes: 54 additions & 0 deletions src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import NextAuth from "next-auth";
import { signIn } from "../../../services/auth";
import CredentialsProvider from "next-auth/providers/credentials";

export default NextAuth({
providers: [
CredentialsProvider({
name: "Sign in with e-mail",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
if (credentials == null) return null;

try {
const { user, jwt } = await signIn({
email: credentials.email,
password: credentials.password
});
return { ...user, jwt };
} catch (error) {
return null;
}
}
})
],
callbacks: {
session: async ({ session, token, user }) => {
if (token) {
session.id = token.id;
session.jwt = token.jwt;
session.user = token.user as {
name?: string | null | undefined;
email?: string | null | undefined;
image?: string | null | undefined;
};
}

return Promise.resolve(session);
},
jwt: async ({ token, user }) => {
const isSignIn = user ? true : false;

if (isSignIn) {
token.id = user.id;
token.jwt = user.jwt;
token.user = user;
}

return Promise.resolve(token);
}
}
});
111 changes: 111 additions & 0 deletions src/pages/auth/signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as yup from "yup";
import Link from "next/link";
import { Input } from "../../components/Form/Input";
import { useEffect } from "react";
import { useRouter } from "next/router";
import { yupResolver } from "@hookform/resolvers/yup";
import { SubmitHandler, useForm } from "react-hook-form";
import { signIn, signOut, useSession } from "next-auth/react";
import { Button, Flex, Stack, Text } from "@chakra-ui/react";

type SignInFormData = {
email: string;
password: string;
};

const signInFormSchema = yup.object().shape({
email: yup.string().required("E-mail obrigatório").email("E-mail inválido"),
password: yup.string().required("Password obrigatório")
});

export default function SignIn() {
const router = useRouter();

const { register, handleSubmit, formState } = useForm({
resolver: yupResolver(signInFormSchema)
});

const { errors } = formState;

const handleSignIn: SubmitHandler<SignInFormData> = async values => {
const result = await signIn("credentials", {
redirect: false,
email: values.email,
password: values.password
});

if (result?.ok) {
router.replace("/dashboard");
return;
}

alert("Credenciais inválidas");
};

const { data: session } = useSession();

useEffect(() => {
if (session === null) return;
console.log("session.jwt...", session?.jwt);
}, [session]);

return (
<Flex w="100vw" h="100vh" alignItems="center" justifyContent="center" flexDirection="column">
<div className="">
<h1>{session ? "Authenticated" : "Not authenticated"}</h1>
<div className="">
{session && (
<>
<h3>Session data</h3>
<p>E-mail: {session.user.email}</p>
<p>JWT from Strapi: Check console</p>
</>
)}
</div>
{session ? (
<button onClick={() => signOut()}>Sign out</button>
) : (
<Link href="/auth/signin">
<button>Sign In</button>
</Link>
)}
</div>

<Flex
as="form"
w="100%"
maxWidth={360}
bg="gray.800"
p="8"
borderRadius={8}
flexDir="column"
onSubmit={handleSubmit(handleSignIn)}
>
<Stack spacing={4}>
<Text fontSize="3xl" fontWeight="bold" letterSpacing="tighter" w="64" align="center" margin="auto" h="12">
SkateHub
<Text color="green.400" as="span" ml="1">
.
</Text>
<Text as="small" color="gray.700" ml="1" fontSize="smaller" fontWeight="300">
dash
</Text>
</Text>
<Flex flexDir="column">
<Input id="email" type="email" label="E-mail" {...register("email")} error={errors.email} />
</Flex>
<Flex flexDir="column">
<Input id="password" type="password" label="Senha" {...register("password")} error={errors.password} />
</Flex>
</Stack>

<Button type="submit" mt="6" colorScheme="green" size="lg" isLoading={formState.isSubmitting}>
Entrar
</Button>
<Text as="a" href="#" color="gray.600" mt="4" align={"center"} textDecoration={"underline"}>
Esqueci minha senha
</Text>
</Flex>
</Flex>
);
}
12 changes: 12 additions & 0 deletions src/services/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import axios from "axios";

const strapiUrl = process.env.STRAPI_URL;

export async function signIn({ email, password }: { email: any; password: any }) {
const res = await axios.post(`${strapiUrl}/api/auth/local`, {
identifier: email,
password
});

return res.data;
}