Commit d389de98 authored by Wellton Quirino's avatar Wellton Quirino

add new modules and areas

parent 1e2e278b
......@@ -10,7 +10,7 @@
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@hookform/resolvers": "^3.9.0",
"@hookform/resolvers": "^3.10.0",
"@mui/material": "^5.16.4",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.2",
......@@ -32,6 +32,7 @@
"lucide-react": "^0.383.0",
"next": "14.2.3",
"next-themes": "^0.3.0",
"nookies": "^2.5.2",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
......@@ -44,6 +45,7 @@
},
"devDependencies": {
"@rocketseat/eslint-config": "^2.2.2",
"@types/cookie": "^0.6.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
......@@ -543,9 +545,10 @@
"integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww=="
},
"node_modules/@hookform/resolvers": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.0.tgz",
"integrity": "sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==",
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
"integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==",
"license": "MIT",
"peerDependencies": {
"react-hook-form": "^7.0.0"
}
......@@ -2273,6 +2276,13 @@
"react": "^18 || ^19"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
......@@ -3162,6 +3172,15 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/cookie": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
......@@ -5571,6 +5590,16 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/nookies": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/nookies/-/nookies-2.5.2.tgz",
"integrity": "sha512-x0TRSaosAEonNKyCrShoUaJ5rrT5KHRNZ5DwPCuizjgrnkpE5DRf3VL7AyyQin4htict92X1EQ7ejDbaHDVdYA==",
"license": "MIT",
"dependencies": {
"cookie": "^0.4.1",
"set-cookie-parser": "^2.4.6"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
......@@ -6480,6 +6509,12 @@
"node": ">=10"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
......
import { api } from '@/lib/axios'
export type AccountsProps = {
id: string
name: string
email: string
}
export async function getAccounts() {
const accounts = await api.get<AccountsProps[]>('/accounts')
return accounts
}
export async function getAccountsId(id: string) {
const account = await api.get<AccountsProps>(`/accounts/${id}`)
return account
}
import { api } from '@/lib/axios'
interface FileBanner {
name: string
}
// interface FileBanner {
// name: string
// }
export type AreasProps = {
id: string
id?: string
name: string
desktopBanner?: { [key: string]: FileBanner }
mobileBanner?: { [key: string]: FileBanner }
desktopBanner: string
mobileBanner: string
// desktopBanner?: { [key: string]: FileBanner }
// mobileBanner?: { [key: string]: FileBanner }
}
export async function createAreas({
......@@ -47,7 +49,6 @@ export async function getAreasId(id: string) {
return area
}
export async function deleteArea(id: string) {
const area = await api.delete(`/areas/${id}`)
return area
......
import { api } from '@/lib/axios'
export type SignInProps = {
email: string
password: string
}
export type SignInResponse = {
access_token: string
user: {
id: string
name: string
email: string
}
}
export async function login(data: SignInProps) {
const token = await api.post<SignInResponse>('/login', data)
return token
}
import { api } from '@/lib/axios'
export type ModulesProps = {
id: string
name: string
description: string
}
export async function createModules({ name, description }: ModulesProps) {
const modules = await api.post<ModulesProps>('/modules', {
name,
description,
})
return modules
}
export async function editModule({ id, name, description }: ModulesProps) {
const modules = await api.patch<ModulesProps[]>(`/modules/${id}`, {
name,
description,
})
return modules
}
export async function getModules() {
const modules = await api.get<ModulesProps[]>('/modules')
return modules
}
export async function getModulesId(id: string) {
const modules = await api.get<ModulesProps>(`/modules/${id}`)
return modules
}
export async function deleteModule(id: string) {
const modules = await api.delete(`/modules/${id}`)
return modules
}
'use client'
import { Search } from 'lucide-react'
import Link from 'next/link'
......@@ -12,8 +14,11 @@ import { SignUp } from '@/components/sign-up'
import { SkeletonSerachParams } from '@/components/skeleton-serach-params'
import { Button } from '@/components/ui/button'
import { Suspense } from 'react'
import { useForm } from 'react-hook-form'
export default function Home() {
const { register } = useForm()
return (
<>
<div className="absolute h-[200px] w-full bg-gradient-to-b from-gray-900 from-5% z-10" />
......@@ -31,6 +36,8 @@ export default function Home() {
type="text"
className="w-full"
themeColor="#fafafa"
name="learned-today"
register={register}
/>
<Search size={24} />
</div>
......
......@@ -16,8 +16,8 @@ export default function Area() {
const router = useRouter()
const data = watch()
const valueDesktopBanner = data?.desktopBanner?.['0']?.name
const valueMobileBanner = data?.mobileBanner?.['0']?.name
const valueDesktopBanner = data?.desktopBanner?.['0']?.name
const queryClient = useQueryClient()
......@@ -31,7 +31,24 @@ export default function Area() {
})
function onSubmit(data: AreasProps) {
mutation.mutate(data)
// console.log('🚀 ~ onSubmit ~ data:', data)
// const fileMobile = data.mobileBanner?.[0]
// const fileDesktop = data.desktopBanner?.[0]
// const formData = new FormData()
// formData.append('mobileBanner', fileMobile, fileMobile?.name)
// formData.append('desktopBanner', fileDesktop, fileDesktop?.name)
// console.log('🚀 ~ onSubmit ~ formData:', formData)
const dataArea = {
...data,
mobileBanner:
'/Users/wellton/Documents/www/sevenpro-frontend/public/images/banners/negocios-mobile.jpg',
desktopBanner:
'/Users/wellton/Documents/www/sevenpro-frontend/public/images/banners/negocios.jpg',
}
console.log('🚀 ~ onSubmit ~ data:', dataArea)
mutation.mutate(dataArea)
}
return (
......@@ -58,7 +75,7 @@ export default function Area() {
<div className="flex flex-col flex-1 mt-6">
<h6 className="text-xl text-purple-100">Banner Desktop</h6>
<span className="mt-4">Dimensões recomendadas: 1512 x 300</span>
<span>Formatos aceitos: .png, .jpg</span>
<span>Formatos aceitos: .png, .jpg, .jpeg</span>
<InputFile
id="desktopBanner"
......@@ -70,7 +87,7 @@ export default function Area() {
<div className="flex flex-col flex-1 mt-6">
<h6 className="text-xl text-purple-100">Banner Mobile</h6>
<span className="mt-4">Dimensões recomendadas: 390 x 300</span>
<span>Formatos aceitos: .png, .jpg</span>
<span>Formatos aceitos: .png, .jpg, .jpeg</span>
<InputFile
id="mobileBanner"
values={valueMobileBanner}
......
......@@ -16,6 +16,7 @@ import { MenuItem } from '@mui/material'
import { format } from 'date-fns'
import { CalendarIcon, PlusIcon, Trash } from 'lucide-react'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
const options = [
{ label: 'The Godfather', value: 1 },
......@@ -24,6 +25,7 @@ const options = [
export default function EditCourse() {
const [date, setDate] = useState<Date>()
const { register } = useForm()
return (
<section className="container py-10">
......@@ -55,6 +57,8 @@ export default function EditCourse() {
variant="standard"
type="text"
themeColor="#26AAA7"
name="name-course"
register={register}
/>
<InputMui
......@@ -63,6 +67,8 @@ export default function EditCourse() {
label="Área"
select
themeColor="#26AAA7"
name="area"
register={register}
>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
......@@ -78,6 +84,8 @@ export default function EditCourse() {
multiline
rows={4}
themeColor="#26AAA7"
name="description-course"
register={register}
/>
<InputMui
......@@ -85,6 +93,8 @@ export default function EditCourse() {
variant="standard"
type="text"
themeColor="#26AAA7"
name="teachers"
register={register}
/>
<div className="flex justify-between flex-wrap gap-6">
......@@ -207,6 +217,8 @@ export default function EditCourse() {
variant="standard"
type="text"
themeColor="#26AAA7"
name="title"
register={register}
/>
<InputMui
......@@ -216,6 +228,8 @@ export default function EditCourse() {
multiline
rows={4}
themeColor="#26AAA7"
name="description"
register={register}
/>
</div>
......
import { StyledInputs } from '@/components/mui/styled-inputs'
import { TextField } from '@mui/material'
import { useForm } from 'react-hook-form'
type FormModulesProps = {
index?: number
remove?: (index: number | number[]) => void
}
export function FormNewModules({ index, remove }: FormModulesProps) {
const { register } = useForm()
return (
<div className="flex flex-col p-4 gap-6 border border-gray-100">
<TextField
label="Título"
variant="standard"
type="text"
sx={StyledInputs({ color: '#26AAA7' })}
{...register(`newModule.${index}.name`)}
/>
<TextField
label="Descrição"
variant="standard"
type="text"
multiline
rows={4}
sx={StyledInputs({ color: '#26AAA7' })}
{...register(`newModule.${index}.description`)}
/>
</div>
)
}
......@@ -3,6 +3,7 @@
import { getAreas } from '@/api/areas'
import { getAudiences } from '@/api/audiences'
import { getCategories } from '@/api/categories'
import { createModules, getModules, ModulesProps } from '@/api/modules'
import InputFile from '@/components/input-file'
import { LoadingSpinIcon } from '@/components/loading-spin-icon'
import { StyledInputs } from '@/components/mui/styled-inputs'
......@@ -25,12 +26,14 @@ import {
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Separator } from '@/components/ui/separator'
import { cn } from '@/lib/utils'
import { zodResolver } from '@hookform/resolvers/zod'
import { Box, Chip, MenuItem, TextField } from '@mui/material'
import { useQuery } from '@tanstack/react-query'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { format } from 'date-fns'
import { CalendarIcon, PlusIcon } from 'lucide-react'
import { CalendarIcon, PlusIcon, Trash2 } from 'lucide-react'
import { useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { z } from 'zod'
// type CoursesProps = {
......@@ -50,8 +53,8 @@ import { z } from 'zod'
// }
const FormSchema = z.object({
name: z.string(),
description: z.string(),
name: z.string().trim().min(3, { message: 'Título obrigatório' }),
description: z.string().trim().min(3, { message: 'Descrição obrigatória' }),
desktopBanner: z.string(),
mobileBanner: z.string(),
duration: z.number(),
......@@ -70,15 +73,18 @@ export default function Curso() {
const [checkedEnd, setCheckedEnd] = useState<boolean>(false)
const [dateStart, setDateStart] = useState<Date>()
const [dateEnd, setDateEnd] = useState<Date>()
const [inputValue, setInputValue] = useState<string>('')
const queryClient = useQueryClient()
// const { form.register, watch, handleSubmit, control } = useForm<CoursesProps>()
const { register, handleSubmit, reset } = useForm<ModulesProps>()
const form = useForm<z.infer<typeof FormSchema>>({
// resolver: zodResolver(FormSchema),
resolver: zodResolver(FormSchema),
defaultValues: {
areaId: '',
professors: [],
moduleIds: [],
},
})
const values = form.watch()
......@@ -90,6 +96,11 @@ export default function Curso() {
queryFn: getAreas,
})
const { data: modules } = useQuery({
queryKey: ['modules'],
queryFn: getModules,
})
const { data: audiences, isLoading: audiencesLoading } = useQuery({
queryKey: ['audiences'],
queryFn: getAudiences,
......@@ -100,15 +111,6 @@ export default function Curso() {
queryFn: getCategories,
})
function onSubmit(data: z.infer<typeof FormSchema>) {
const body = {
...data,
startDate: dateStart?.toLocaleDateString() || '',
endDate: dateEnd?.toLocaleDateString() || '',
}
console.log(body)
}
function onCheckedStart(checkedStart: boolean) {
setCheckedStart(checkedStart)
}
......@@ -132,6 +134,55 @@ export default function Curso() {
)
}
const mutation = useMutation({
mutationFn: createModules,
onSuccess: () => {
toast.success('Módulo criado com sucesso!')
queryClient.invalidateQueries({ queryKey: ['modules'] })
},
})
async function addNewModuler(data: ModulesProps) {
const response = await mutation.mutateAsync(data)
const responseModuleId = response.data.id
form.setValue('moduleIds', [...selectedIds, responseModuleId])
reset()
}
const selectedIds = form.watch('moduleIds') || []
const selectedModules = modules?.data.filter((module) =>
selectedIds.includes(module.id),
)
const handleSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedId = event.target.value
if (selectedId && !selectedIds.includes(selectedId)) {
form.setValue('moduleIds', [...selectedIds, selectedId])
}
}
const handleDeleteModule = (idToRemove: string) => {
form.setValue(
'moduleIds',
selectedIds.filter((id) => id !== idToRemove),
)
}
function onSubmit(data: z.infer<typeof FormSchema>) {
const body = {
...data,
startDate: dateStart?.toLocaleDateString() || '',
endDate: dateEnd?.toLocaleDateString() || '',
}
console.log(body)
}
console.log('error: ', form.formState.errors)
return (
<section className="container py-10">
<Form {...form}>
......@@ -155,6 +206,9 @@ export default function Curso() {
{...form.register('name')}
sx={StyledInputs({ color: '#26AAA7' })}
/>
{form.formState.errors.name && (
<p>{form.formState.errors.name.message}</p>
)}
<TextField
type="text"
......@@ -185,14 +239,6 @@ export default function Curso() {
{...form.register('description')}
/>
{/* <TextField
label="Nome dos(as) Professores(as):"
variant="standard"
type="text"
sx={StyledInputs({ color: '#26AAA7' })}
{...form.register('professors')}
/> */}
<Controller
control={form.control}
name="professors"
......@@ -274,6 +320,7 @@ export default function Curso() {
<PopoverTrigger asChild>
<Button
variant={'ghost'}
type="button"
className={cn(
'lg:w-[450px] justify-start text-left font-normal border-b rounded-none pl-0 border-green-400',
!dateStart && 'text-muted-foreground',
......@@ -293,6 +340,7 @@ export default function Curso() {
selected={dateStart}
onSelect={setDateStart}
initialFocus
{...form.register('startDate')}
/>
</PopoverContent>
</Popover>
......@@ -316,6 +364,7 @@ export default function Curso() {
<PopoverTrigger asChild>
<Button
variant={'ghost'}
type="button"
className={cn(
'lg:w-[450px] justify-start text-left font-normal border-b rounded-none pl-0 border-green-400',
!dateEnd && 'text-muted-foreground',
......@@ -348,12 +397,47 @@ export default function Curso() {
<h6 className="text-xl text-purple-100 mb-4">Módulos</h6>
<TextField
type="text"
variant="standard"
label="Selecione os módulos do curso"
select
sx={StyledInputs({ color: '#26AAA7' })}
defaultValue={''}
value=""
onChange={handleSelect}
// {...form.register('moduleIds')}
>
<MenuItem value="" className="hidden">
Selecione os módulos
</MenuItem>
{modules?.data.map((option) => (
<MenuItem key={option.id} value={option.id}>
{option.name}
</MenuItem>
))}
</TextField>
{selectedModules?.map((item) => (
<div
key={item.id}
className="w-full flex justify-between bg-green-800/30 py-2 px-4 rounded-sm"
>
<p>{item.name}</p>
<Trash2
className="cursor-pointer text-red-400 hover:text-red-100"
onClick={() => handleDeleteModule(item.id)}
/>
</div>
))}
<div className="flex flex-col p-4 gap-6 border border-gray-100">
<TextField
label="Título"
variant="standard"
type="text"
sx={StyledInputs({ color: '#26AAA7' })}
{...register('name')}
/>
<TextField
......@@ -363,16 +447,20 @@ export default function Curso() {
multiline
rows={4}
sx={StyledInputs({ color: '#26AAA7' })}
{...register('description')}
/>
</div>
<Button
variant="third"
type="button"
className="uppercase w-52 flex items-center gap-2 !mt-8"
onClick={handleSubmit(addNewModuler)}
>
<PlusIcon size={20} /> <span>Adicionar Módulo</span>
<PlusIcon size={20} /> <span>Criar novo Módulo</span>
</Button>
</div>
<div className="w-[380px] pl-6 flex flex-col gap-4">
<h6 className="text-xl text-purple-100 mb-4">
Qual o público alvo?
......
......@@ -4,10 +4,13 @@ import { getAreas } from '@/api/areas'
import { Card } from '@/components/card'
import { InputMui } from '@/components/mui/inputs'
import { Button } from '@/components/ui/button'
import { AuthContext } from '@/contexts/auth-context'
import { CoursesCard } from '@/utils/courses-array'
import { useQuery } from '@tanstack/react-query'
import { Pencil, Search } from 'lucide-react'
import Link from 'next/link'
import { useContext } from 'react'
import { useForm } from 'react-hook-form'
// const areas = [
// {
......@@ -46,6 +49,10 @@ const banners = [
]
export default function Admin() {
const { register } = useForm()
const { user } = useContext(AuthContext)
console.log('🚀 ~ Admin ~ user:', user)
const {
data: areas,
error,
......@@ -63,7 +70,7 @@ export default function Admin() {
return (
<section className="container">
<h1 className="text-green-400 text-2xl">Bem vindo, João da Silva,</h1>
<h1 className="text-green-400 text-2xl">Bem vindo, {user?.name}!</h1>
<div className="flex h-auto mb-20">
<aside className="mt-10 w-[348px]">
<h2 className="text-purple-50 text-2xl">Áreas</h2>
......@@ -116,6 +123,8 @@ export default function Admin() {
variant="standard"
className="w-full"
themeColor="#fafafa"
name="search"
register={register}
/>
<Search size={24} className="-ml-6" />
</div>
......
......@@ -2,6 +2,7 @@ import { InputMui } from '@/components/mui/inputs'
import { Button } from '@/components/ui/button'
import { MenuItem } from '@mui/material'
import { Clock4, Mail, Smartphone } from 'lucide-react'
import { useForm } from 'react-hook-form'
const matters = [
{
......@@ -23,6 +24,8 @@ const matters = [
]
export default function Contact() {
const { register } = useForm()
return (
<main className="container flex flex-col justify-center items-center py-20">
<h1 className="text-center font-normal md:font-extrabold text-green-800 text-2xl md:text-4xl">
......@@ -53,16 +56,31 @@ export default function Contact() {
Envie uma mensagem
</h2>
<form action="" className="mt-6 flex flex-col gap-6">
<InputMui type="text" variant="outlined" label="Nome completo" />
<InputMui
type="text"
variant="outlined"
label="Nome completo"
name="full-name"
register={register}
/>
<InputMui
type="text"
variant="outlined"
label="Mensagem"
multiline
rows={4}
name="message"
register={register}
/>
<InputMui type="text" variant="outlined" label="Assunto" select>
<InputMui
type="text"
variant="outlined"
label="Assunto"
select
name="subject"
register={register}
>
{matters.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
......
......@@ -7,6 +7,7 @@ import type { Metadata } from 'next'
import { Poppins } from 'next/font/google'
import { Toaster } from 'sonner'
import { AuthProvider } from '@/contexts/auth-context'
import ReactQueryProvider from '@/providers/react-query'
const poppins = Poppins({ subsets: ['latin'], weight: ['300', '400', '800'] })
......@@ -24,6 +25,7 @@ export default function RootLayout({
return (
<html lang="pt-BR">
<body className={`antialiased ${poppins.className}`}>
<AuthProvider>
<ThemeProvider
attribute="class"
defaultTheme="dark"
......@@ -39,6 +41,7 @@ export default function RootLayout({
</ReactQueryProvider>
</StyledEngineProvider>
</ThemeProvider>
</AuthProvider>
</body>
</html>
)
......
'use client'
import { InputMui } from '@/components/mui/inputs'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { AuthContext } from '@/contexts/auth-context'
import Image from 'next/image'
import Link from 'next/link'
// import { useRouter } from 'next/router'
import { AxiosError } from 'axios'
import { useContext } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import globo from '../../../public/images/globo.svg'
type FormLogin = {
email: string
password: string
}
export default function Login() {
const { handleSubmit, register } = useForm<FormLogin>()
const { signIn } = useContext(AuthContext)
// const router = useRouter()
const onSubmit = async (data: FormLogin) => {
try {
await signIn(data)
// router.push('/admin')
} catch (error) {
if (error instanceof AxiosError) {
const { message } = error.response?.data
toast.error(`${message}`)
}
console.log('🚀 ~ onSubmit ~ error aqui:', error)
}
}
return (
<main>
<section className=" container flex flex-col justify-center items-center space-y-10 h-[723px]">
......@@ -20,15 +50,27 @@ export default function Login() {
Acesso exclusivo para usuários cadastrados.
</h2>
<form
// onSubmit={handleSubmit(onSubmit)}
onSubmit={handleSubmit(onSubmit)}
className="w-full md:w-[600px] flex flex-col gap-8 order-4"
>
<div className="flex flex-col space-y-6">
<div className="flex flex-col space-y-4">
<InputMui label="E-mail" variant="outlined" type="email" />
<InputMui label="Senha" variant="outlined" type="password" />
<InputMui
label="E-mail"
variant="outlined"
type="email"
name="email"
register={register}
/>
<InputMui
label="Senha"
variant="outlined"
type="password"
name="password"
register={register}
/>
</div>
<div className="flex items-center space-x-2">
{/* <div className="flex items-center space-x-2">
<Checkbox id="remember" />
<label
htmlFor="remember"
......@@ -36,9 +78,10 @@ export default function Login() {
>
Lembrar-me
</label>
</div>
<Button className="rounded-sm" asChild>
<Link href="/admin">Entrar</Link>
</div> */}
<Button className="rounded-sm" type="submit">
{/* <Link href="/admin">Entrar</Link> */}
Entrar
</Button>
<Button
variant={'link'}
......
......@@ -17,6 +17,7 @@ const InputFile = forwardRef<HTMLInputElement, InputFileProps>(
<input
id={id}
type="file"
accept="image/png, image/jpeg, image/jpg"
className="hidden"
ref={ref}
{...inputProps}
......
......@@ -3,21 +3,26 @@
import { useThemeClient } from '@/hooks/useThemeClient'
import { TextField, TextFieldProps } from '@mui/material'
import { ReactNode } from 'react'
import { FieldValues, Path, UseFormRegister } from 'react-hook-form'
type InputMuiProps = TextFieldProps & {
type InputMuiProps<T extends FieldValues> = TextFieldProps & {
label: string
variant: 'standard' | 'filled' | 'outlined'
themeColor?: string
children?: ReactNode
name: Path<T>
register: UseFormRegister<T>
}
export function InputMui({
export const InputMui = <T extends FieldValues>({
label,
variant,
children,
themeColor = '#fafafa',
...props
}: InputMuiProps) {
register,
name,
...rest
}: InputMuiProps<T>) => {
const themeConfig = useThemeClient()
const color = themeConfig === 'dark' ? themeColor : '#3C3C3C'
......@@ -26,7 +31,8 @@ export function InputMui({
<TextField
label={label}
variant={variant}
{...props}
{...register(name)}
{...rest}
sx={{
'& .MuiOutlinedInput-root': {
'& fieldset': {
......
import { Search } from 'lucide-react'
import { useForm } from 'react-hook-form'
import { InputMui } from './mui/inputs'
import { Label } from './ui/label'
import {
......@@ -11,6 +12,8 @@ import {
} from './ui/select'
export default function SearchFilter() {
const { register } = useForm()
return (
<div className="container grid md:grid-cols-2 gap-8 items-end mb-8">
<div className="flex items-center max-w-[712px] flex-1">
......@@ -19,6 +22,8 @@ export default function SearchFilter() {
variant="standard"
type="text"
className="w-full #fafafa"
name="learned-today"
register={register}
/>
<Search size={24} className="-ml-6" />
</div>
......
'use client'
// import { useRouter } from 'next/router'
import { parseCookies, setCookie } from 'nookies'
import { createContext, useEffect, useState } from 'react'
import { getAccountsId } from '@/api/accounts'
import { login } from '@/api/login'
import { api } from '@/lib/axios'
import { useRouter } from 'next/navigation'
type User = {
id: string
name: string
email: string
} | null
type SignInData = {
email: string
password: string
}
type AuthContextType = {
isAuthenticated: boolean
user: User
signIn: (data: SignInData) => Promise<void>
}
export const AuthContext = createContext({} as AuthContextType)
export function AuthProvider({ children }) {
const [user, setUser] = useState<User | null>(null)
const router = useRouter()
const isAuthenticated = !!user
useEffect(() => {
const { 'sevenpro-token': token } = parseCookies()
const { 'sevenpro-user': userCookie } = parseCookies()
if (token && userCookie) {
const { id } = JSON.parse(userCookie)
getAccountsId(id).then((response) => {
setUser(response.data)
})
}
}, [])
async function signIn({ email, password }: SignInData) {
const { data } = await login({
email,
password,
})
const token = data.access_token
const user = data.user
setCookie(undefined, 'sevenpro-token', token, {
maxAge: 60 * 60 * 1, // 1 hour
})
setCookie(undefined, 'sevenpro-user', JSON.stringify(user), {
maxAge: 60 * 60 * 1, // 1 hour
})
api.defaults.headers.Authorization = `Bearer ${token}`
setUser(user)
router.push('/admin')
}
return (
<AuthContext.Provider value={{ user, isAuthenticated, signIn }}>
{children}
</AuthContext.Provider>
)
}
import axios from 'axios'
// import Router from 'next/router'
import { destroyCookie, parseCookies } from 'nookies'
const { 'sevenpro-token': token } = parseCookies()
console.log('🚀 ~ token:', token)
export const api = axios.create({
baseURL: 'http://localhost:3000',
baseURL: process.env.NEXT_PUBLIC_URL_API,
})
if (token) {
api.defaults.headers.Authorization = `Bearer ${token}`
}
// api.interceptors.request.use(
// (config) => {
// return config
// },
// (error) => {
// return Promise.reject(error)
// },
// )
api.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response && error.response.status === 401) {
// Remove o token inválido
destroyCookie(null, 'sevenpro-token')
// Redireciona para a página de login
// Router.push('/login')
}
return Promise.reject(error)
},
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment