From 2ca2c57d24ecf8d3972809b8787e418df6833170 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 15:17:29 -0400 Subject: [PATCH 01/33] fix: typing and lint --- src/contexts/SidebarDrawerContext.tsx | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/contexts/SidebarDrawerContext.tsx b/src/contexts/SidebarDrawerContext.tsx index 567ac79..bef9d91 100644 --- a/src/contexts/SidebarDrawerContext.tsx +++ b/src/contexts/SidebarDrawerContext.tsx @@ -1,29 +1,31 @@ -import { useDisclosure, UseDisclosureReturn } from "@chakra-ui/hooks"; -import { useRouter } from "next/router"; -import { createContext, ReactNode, useContext, useEffect } from "react"; +import { useDisclosure, UseDisclosureReturn } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import { createContext, ReactNode, useContext, useEffect } from 'react'; interface SidebarDrawerProviderProps { - children: ReactNode + children: ReactNode; } -type SidebarDrwaerContextData = UseDisclosureReturn +type SidebarDrawerContextData = UseDisclosureReturn; -const SidebarDrawerContext = createContext({} as SidebarDrwaerContextData) +const SidebarDrawerContext = createContext({} as SidebarDrawerContextData); -export function SidebarDrawerProvider({ children }: SidebarDrawerProviderProps) { - const disclosure = useDisclosure() - const router = useRouter() +export function SidebarDrawerProvider({ + children, +}: SidebarDrawerProviderProps): JSX.Element { + const disclosure = useDisclosure(); + const router = useRouter(); useEffect(() => { - disclosure.onClose() - }, [router.asPath]) + disclosure.onClose(); + }, [disclosure]); return ( {children} - - ) + ); } -export const useSidebarDrawer = () => useContext(SidebarDrawerContext) \ No newline at end of file +export const useSidebarDrawer = (): SidebarDrawerContextData => + useContext(SidebarDrawerContext); From b4674628784ce57b9b5fd2e796124aa3dc802602 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:27:49 -0400 Subject: [PATCH 02/33] refactoring: fix bad behavior of isDark props --- src/components/Input.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 26d700e..747b973 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -32,14 +32,13 @@ const InputBase: ForwardRefRenderFunction = ( justifyContent="center" isInvalid={!!error} maxW="450px" - mb="6" > {label} @@ -55,13 +54,15 @@ const InputBase: ForwardRefRenderFunction = ( name={name} fontSize="xl" ref={ref} + color="main.darkBlue" + borderWidth="3px" borderColor="transparent" - bgColor={isDark ? 'main.darkBlue' : 'main.offWhite'} + bgColor="main.offWhite" _placeholder={{ - color: isDark ? 'main.white' : 'main.darkBlue', + color: 'main.darkBlue', }} _hover={{ - borderColor: isDark ? 'main.offWhite' : 'main.green', + borderColor: isDark ? 'main.green' : 'main.darkBlue', }} borderRadius="20px" py={6} From 4c684ea9282a729e5b96a84301b1ab4c06461f1c Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:28:19 -0400 Subject: [PATCH 03/33] refactoring: change icons --- src/components/Sidebar/SidebarNav.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 3c9acf1..24c5210 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -1,9 +1,9 @@ import { Stack } from '@chakra-ui/react'; import { - RiDashboardLine, - RiTBoxLine, - RiFileEditLine, - RiSettingsLine, + RiDashboardFill, + RiArchiveFill, + RiDraftFill, + RiSettings5Fill, } from 'react-icons/ri'; import { SidebarLink } from './SidebarLink'; import { SidebarProfile } from './SidebarProfile'; @@ -13,10 +13,10 @@ export function SidebarNav(): JSX.Element { - Dashboard - Estoque - Cadastro - Configurções + Dashboard + Estoque + Cadastro + Configurções ); } From 730e3c6dd1e65afd0584626940196ec79af9e483 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:29:35 -0400 Subject: [PATCH 04/33] assets: add svg assets to estoque and item pages --- public/images/closedbox.svg | 5 +++++ public/images/openedbox.svg | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 public/images/closedbox.svg create mode 100644 public/images/openedbox.svg diff --git a/public/images/closedbox.svg b/public/images/closedbox.svg new file mode 100644 index 0000000..7ab1852 --- /dev/null +++ b/public/images/closedbox.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/openedbox.svg b/public/images/openedbox.svg new file mode 100644 index 0000000..814ee42 --- /dev/null +++ b/public/images/openedbox.svg @@ -0,0 +1,9 @@ + + + + + + + + + From ce9cb540e745ec26f0d212304da1fde847172121 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:30:09 -0400 Subject: [PATCH 05/33] fix: refactoring bad behavior of input isDark props --- src/pages/login.tsx | 10 +++--- src/pages/signup.tsx | 86 ++++++++++++++++++++++++-------------------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/pages/login.tsx b/src/pages/login.tsx index fcee357..f9929e7 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,4 +1,4 @@ -import { Grid, Image, Flex, Box } from '@chakra-ui/react'; +import { Grid, Image, Flex, Box, VStack } from '@chakra-ui/react'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; @@ -19,9 +19,11 @@ export default function Login(): JSX.Element { justifyContent="center" > - - - + + + + + ); diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index 2275431..b387abf 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -1,4 +1,4 @@ -import { Grid, Box, Flex, Image, useToast } from '@chakra-ui/react'; +import { Grid, Box, Flex, Image, useToast, VStack } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; import { useRouter } from 'next/router'; @@ -112,44 +112,52 @@ export default function SignUp(): JSX.Element { onSubmit={handleSubmit(onSubmit, onError)} > - - - - - - + + + + + + + + From ad84ea2d1d32b35500a6e8955930b07c86c382dc Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:30:30 -0400 Subject: [PATCH 06/33] create modal to register items --- src/components/ModalRegisterItem.tsx | 133 +++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/components/ModalRegisterItem.tsx diff --git a/src/components/ModalRegisterItem.tsx b/src/components/ModalRegisterItem.tsx new file mode 100644 index 0000000..624c403 --- /dev/null +++ b/src/components/ModalRegisterItem.tsx @@ -0,0 +1,133 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Flex, + VStack, +} from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { useMutation } from 'react-query'; +import { api } from '../services/api'; +import { queryClient } from '../services/queryClient'; + +import { Button } from './Button'; +import { Input } from './Input'; + +interface ModalRegisterItemProps { + isOpen: boolean; + onClose: () => void; +} + +interface CreateItemFormData { + code?: string; + name: string; + category: string; + minimumStock?: number; + daysToNotifyExpirationDate?: number; + image?: string; + measureUnity: string; +} + +export function ModalRegisterItem({ + isOpen, + onClose, +}: ModalRegisterItemProps): JSX.Element { + const { + handleSubmit, + register, + formState: { errors }, + } = useForm(); + + const createItem = useMutation( + async ({ + name, + category, + minimumStock, + daysToNotifyExpirationDate, + measureUnity, + }: CreateItemFormData) => { + const response = await api.post('items', { + name, + category, + minimumStock, + daysToNotifyExpirationDate, + measureUnity, + }); + + return response.data.user; + }, + { + onSuccess: () => { + queryClient.invalidateQueries('items'); + }, + } + ); + + const onSubmit = async (item: CreateItemFormData): Promise => { + try { + await createItem.mutateAsync(item); + } catch (err) { + console.log(err); + } + }; + return ( + + + + + Cadastro de produto + + + + + + + + + + + + + + + + + + ); +} From 67412bcb86c9a577eb642e49eb4e581c6b5e36f5 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Thu, 17 Jun 2021 16:30:51 -0400 Subject: [PATCH 07/33] create page of products --- src/pages/products.tsx | 238 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 src/pages/products.tsx diff --git a/src/pages/products.tsx b/src/pages/products.tsx new file mode 100644 index 0000000..bd1a8ba --- /dev/null +++ b/src/pages/products.tsx @@ -0,0 +1,238 @@ +import { + Flex, + useDisclosure, + Heading, + Text, + HStack, + Table, + Thead, + Th, + Tbody, + Tr, + Td, + Image, + Box, +} from '@chakra-ui/react'; +import { ModalRegisterItem } from '../components/ModalRegisterItem'; +import { Button } from '../components/Button'; +import { Sidebar } from '../components/Sidebar'; + +export default function Products(): JSX.Element { + const { isOpen, onClose, onOpen } = useDisclosure(); + return ( + + + + + + Cadastro de produtos + + + + + Lista de produtos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CódigoNomeCategoriaTipoDataQtdeUndValorCriado em
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
+
+
+ +
+ ); +} From 83da3ce65d59abcbe92d674ba552d38dd2a11acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeandroGon=C3=A7alves?= Date: Thu, 17 Jun 2021 18:20:04 -0300 Subject: [PATCH 08/33] create home page --- public/images/personBackground.svg | 198 +++++++++++++++++++++++++++++ src/pages/home.tsx | 125 ++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 public/images/personBackground.svg create mode 100644 src/pages/home.tsx diff --git a/public/images/personBackground.svg b/public/images/personBackground.svg new file mode 100644 index 0000000..cb1c01a --- /dev/null +++ b/public/images/personBackground.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/pages/home.tsx b/src/pages/home.tsx new file mode 100644 index 0000000..bf6693b --- /dev/null +++ b/src/pages/home.tsx @@ -0,0 +1,125 @@ +import { + Box, + Stack, + Text, + Image, + Flex, + HStack, + Button, +} from '@chakra-ui/react'; +import Link from 'next/link'; + +export default function Home(): JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Título ou texto chamativo + + + Paraágrafo que acompanha o título out texto chamativo. blablabla + blablabla blablabla blablablabla bla blabla blablavbla blablabla + blablabla blablabla blablablabla bla blabla blablavbla blablabla + blablabla blablabla blablablabla bla blabla blablavbla + + + + ); +} From 38f759b5ff37ba72fc7a6684a21c38d897c46ffa Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 12:20:57 -0400 Subject: [PATCH 09/33] Create authentication flow and refactoring input component and home page --- package.json | 1 + src/components/Input.tsx | 16 ++- src/components/ModalRegisterItem.tsx | 2 +- src/components/providers/AppProvider.tsx | 14 +++ src/contexts/AuthContext.tsx | 113 ++++++++++++++++++ src/pages/_app.tsx | 6 +- src/pages/dashboard.tsx | 11 ++ src/pages/home.tsx | 125 -------------------- src/pages/index.tsx | 143 ++++++++++++++++++----- src/pages/login.tsx | 59 +++++++++- src/pages/products.tsx | 7 ++ src/pages/signup.tsx | 8 +- src/services/api.ts | 37 +++++- src/services/apiClient.ts | 3 + src/services/errors/AuthTokenError.ts | 5 + src/utils/withSSRAuth.ts | 46 ++++++++ yarn.lock | 18 +++ 17 files changed, 446 insertions(+), 168 deletions(-) create mode 100644 src/components/providers/AppProvider.tsx create mode 100644 src/contexts/AuthContext.tsx create mode 100644 src/pages/dashboard.tsx delete mode 100644 src/pages/home.tsx create mode 100644 src/services/apiClient.ts create mode 100644 src/services/errors/AuthTokenError.ts create mode 100644 src/utils/withSSRAuth.ts diff --git a/package.json b/package.json index 6bb1709..8888364 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "axios": "^0.21.1", "framer-motion": "^4", "next": "11.0.0", + "nookies": "^2.5.2", "react": "17.0.2", "react-dom": "17.0.2", "react-hook-form": "^7.8.8", diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 747b973..95428a1 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -10,6 +10,7 @@ import { InputProps as ChakraInputProps, Tooltip, InputRightElement, + InputLeftElement, } from '@chakra-ui/react'; import { FiAlertCircle } from 'react-icons/fi'; @@ -18,10 +19,11 @@ interface InputProps extends ChakraInputProps { label?: string; error?: FieldError; isDark?: boolean; + leftInputElement?: string; } const InputBase: ForwardRefRenderFunction = ( - { name, isDark = false, label, error = null, ...rest }, + { name, isDark = false, label, error = null, leftInputElement, ...rest }, ref ) => { return ( @@ -48,8 +50,16 @@ const InputBase: ForwardRefRenderFunction = ( alignItems="center" justifyContent="center" > + {leftInputElement && ( + + + {leftInputElement} + + + )} + = ( borderColor: isDark ? 'main.green' : 'main.darkBlue', }} borderRadius="20px" - py={6} + py={8} pr={8} {...rest} /> diff --git a/src/components/ModalRegisterItem.tsx b/src/components/ModalRegisterItem.tsx index 624c403..adf7ae2 100644 --- a/src/components/ModalRegisterItem.tsx +++ b/src/components/ModalRegisterItem.tsx @@ -10,7 +10,7 @@ import { } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; -import { api } from '../services/api'; +import { api } from '../services/apiClient'; import { queryClient } from '../services/queryClient'; import { Button } from './Button'; diff --git a/src/components/providers/AppProvider.tsx b/src/components/providers/AppProvider.tsx new file mode 100644 index 0000000..3d3ec43 --- /dev/null +++ b/src/components/providers/AppProvider.tsx @@ -0,0 +1,14 @@ +import { AuthProvider } from '../../contexts/AuthContext'; +import { SidebarDrawerProvider } from '../../contexts/SidebarDrawerContext'; + +interface AppProviderProps { + children: React.ReactElement; +} + +export function AppProvider({ children }: AppProviderProps): JSX.Element { + return ( + + {children} + + ); +} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx new file mode 100644 index 0000000..a79ec08 --- /dev/null +++ b/src/contexts/AuthContext.tsx @@ -0,0 +1,113 @@ +import { createContext, ReactNode, useEffect, useState } from 'react'; +import { setCookie, parseCookies, destroyCookie } from 'nookies'; +import Router from 'next/router'; +// eslint-disable-next-line import/no-cycle +import { api } from '../services/apiClient'; + +type User = { + id: string; + name: string; + email: string; + login: string; + phone: string; +}; + +type SignInCredentials = { + login: string; + password: string; +}; + +type AuthContextData = { + signIn(credentials: SignInCredentials): Promise; + signOut: () => void; + user: User; + isAuthenticated: boolean; +}; + +type AuthProviderProps = { + children: ReactNode; +}; + +export const AuthContext = createContext({} as AuthContextData); + +let authChannel: BroadcastChannel; + +export function signOut(): void { + destroyCookie(undefined, '@openwms.token'); + + authChannel.postMessage('signOut'); + + Router.push('/'); +} + +export function AuthProvider({ children }: AuthProviderProps): JSX.Element { + const [user, setUser] = useState(null); + const isAuthenticated = !!user; + + useEffect(() => { + authChannel = new BroadcastChannel('auth'); + + authChannel.onmessage = message => { + switch (message.data) { + case 'signOut': + Router.push('/login'); + break; + case 'signIn': + Router.push('/dashboard'); + break; + default: + break; + } + }; + }, []); + + // useEffect(() => { + // const { '@openwms.token': token } = parseCookies(); + + // if (token) { + // api + // .get('/me') + // .then(response => { + // const { email, permissions, roles } = response.data; + + // setUser({ email, permissions, roles }); + // }) + // .catch(() => { + // signOut(); + // }); + // } + // }, []); + + async function signIn({ login, password }: SignInCredentials): Promise { + try { + const response = await api.post('sessions', { + login, + password, + }); + + console.log(response); + + const { token, user: userData } = response.data; + + setCookie(undefined, '@openwms.token', token, { + maxAge: 60 * 60 * 24, // 1 day + path: '/', + }); + + setUser(userData); + + api.defaults.headers.Authorization = `Bearer ${token}`; + + Router.push('/dashboard'); + authChannel.postMessage('signIn'); + } catch (err) { + console.log(err); + } + } + + return ( + + {children} + + ); +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 77ed584..39f8c08 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -3,15 +3,15 @@ import { ChakraProvider } from '@chakra-ui/react'; import { QueryClientProvider } from 'react-query'; import { theme } from '../styles/theme'; import { queryClient } from '../services/queryClient'; -import { SidebarDrawerProvider } from '../contexts/SidebarDrawerContext'; +import { AppProvider } from '../components/providers/AppProvider'; function MyApp({ Component, pageProps }: AppProps): JSX.Element { return ( - + - + ); diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx new file mode 100644 index 0000000..d690d2a --- /dev/null +++ b/src/pages/dashboard.tsx @@ -0,0 +1,11 @@ +import { withSSRAuth } from '../utils/withSSRAuth'; + +export default function Dashboard(): JSX.Element { + return

Dashboard

; +} + +export const getServerSideProps = withSSRAuth(async ctx => { + return { + props: {}, + }; +}); diff --git a/src/pages/home.tsx b/src/pages/home.tsx deleted file mode 100644 index bf6693b..0000000 --- a/src/pages/home.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { - Box, - Stack, - Text, - Image, - Flex, - HStack, - Button, -} from '@chakra-ui/react'; -import Link from 'next/link'; - -export default function Home(): JSX.Element { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Título ou texto chamativo - - - Paraágrafo que acompanha o título out texto chamativo. blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla - - - - ); -} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 89b061f..bf6693b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,34 +1,125 @@ -import { Flex, Icon, IconButton, useBreakpointValue } from '@chakra-ui/react'; -import { RiMenuLine } from 'react-icons/ri'; -import { Sidebar } from '../components/Sidebar'; -import { useSidebarDrawer } from '../contexts/SidebarDrawerContext'; +import { + Box, + Stack, + Text, + Image, + Flex, + HStack, + Button, +} from '@chakra-ui/react'; +import Link from 'next/link'; export default function Home(): JSX.Element { - const { onOpen } = useSidebarDrawer() + return ( + + + + + + + - const isWideVersion = useBreakpointValue({ - base: false, - lg: true - }) + + + - return ( - - { !isWideVersion && ( - } - fontSize='28' - variant='unstyled' - onClick={onOpen} - mr='2' - > + + + - - )} + + + + - - + + + + + - - ) + + + Título ou texto chamativo + + + Paraágrafo que acompanha o título out texto chamativo. blablabla + blablabla blablabla blablablabla bla blabla blablavbla blablabla + blablabla blablabla blablablabla bla blabla blablavbla blablabla + blablabla blablabla blablablabla bla blabla blablavbla + + + + ); } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index f9929e7..f0144b7 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,8 +1,49 @@ -import { Grid, Image, Flex, Box, VStack } from '@chakra-ui/react'; +import { Grid, Image, Flex, Box, VStack, useToast } from '@chakra-ui/react'; +import { useContext } from 'react'; +import { useForm } from 'react-hook-form'; +import { useMutation } from 'react-query'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; +import { AuthContext } from '../contexts/AuthContext'; + +interface SignInFormData { + login: string; + password: string; +} export default function Login(): JSX.Element { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + const toast = useToast(); + + const { signIn } = useContext(AuthContext); + + const authenticate = useMutation( + async ({ login, password }: SignInFormData): Promise => { + await signIn({ login, password }); + } + ); + + const onSubmit = async ({ + login, + password, + }: SignInFormData): Promise => { + try { + await authenticate.mutateAsync({ login, password }); + } catch (err) { + toast({ + duration: 3000, + status: 'error', + title: 'Algo deu errado', + description: 'Usuário/senha incorretos!', + position: 'top-right', + }); + } + }; return ( - - + + - + ); diff --git a/src/pages/products.tsx b/src/pages/products.tsx index bd1a8ba..ee4a492 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -16,6 +16,7 @@ import { import { ModalRegisterItem } from '../components/ModalRegisterItem'; import { Button } from '../components/Button'; import { Sidebar } from '../components/Sidebar'; +import { withSSRAuth } from '../utils/withSSRAuth'; export default function Products(): JSX.Element { const { isOpen, onClose, onOpen } = useDisclosure(); @@ -236,3 +237,9 @@ export default function Products(): JSX.Element { ); } + +export const getServerSideProps = withSSRAuth(async ctx => { + return { + props: {}, + }; +}); diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index b387abf..af9ddb0 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -6,7 +6,7 @@ import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; import { Input } from '../components/Input'; import { Button } from '../components/Button'; -import { api } from '../services/api'; +import { api } from '../services/apiClient'; import { queryClient } from '../services/queryClient'; interface ICreateUserFormData { @@ -77,7 +77,10 @@ export default function SignUp(): JSX.Element { const onSubmit = async (user: ICreateUserFormData): Promise => { try { - await createUser.mutateAsync(user); + await createUser.mutateAsync({ + ...user, + phone: `+55${user.phone}`, + }); toast({ duration: 3000, @@ -137,6 +140,7 @@ export default function SignUp(): JSX.Element { { + return response; + }, + (error: AxiosError) => { + if (process.browser) { + signOut(); + } else { + return Promise.reject(new AuthTokenError()); + } + + return Promise.reject(error); + } + ); + + return api; +} diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts new file mode 100644 index 0000000..2a39f99 --- /dev/null +++ b/src/services/apiClient.ts @@ -0,0 +1,3 @@ +import { setupApiClient } from './api'; + +export const api = setupApiClient(); diff --git a/src/services/errors/AuthTokenError.ts b/src/services/errors/AuthTokenError.ts new file mode 100644 index 0000000..71da98e --- /dev/null +++ b/src/services/errors/AuthTokenError.ts @@ -0,0 +1,5 @@ +export class AuthTokenError extends Error { + constructor() { + super('Erro com o token de autenticação'); + } +} diff --git a/src/utils/withSSRAuth.ts b/src/utils/withSSRAuth.ts new file mode 100644 index 0000000..16a6b66 --- /dev/null +++ b/src/utils/withSSRAuth.ts @@ -0,0 +1,46 @@ +import { + GetServerSideProps, + GetServerSidePropsContext, + GetServerSidePropsResult, +} from 'next'; +import { destroyCookie, parseCookies } from 'nookies'; +import { AuthTokenError } from '../services/errors/AuthTokenError'; + +export function withSSRAuth

(fn: GetServerSideProps

): GetServerSideProps { + return async ( + ctx: GetServerSidePropsContext + ): Promise> => { + const cookies = parseCookies(ctx); + const token = cookies['@openwms.token']; + if (!token) { + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; + } + + try { + return await fn(ctx); + } catch (err) { + if (err instanceof AuthTokenError) { + destroyCookie(ctx, '@openwms.token'); + + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; + } + } + + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; + }; +} diff --git a/yarn.lock b/yarn.lock index 3b3a72a..6a01c21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2549,6 +2549,11 @@ convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, dependencies: safe-buffer "~5.1.1" +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -5273,6 +5278,14 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +nookies@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/nookies/-/nookies-2.5.2.tgz#cc55547efa982d013a21475bd0db0c02c1b35b27" + integrity sha512-x0TRSaosAEonNKyCrShoUaJ5rrT5KHRNZ5DwPCuizjgrnkpE5DRf3VL7AyyQin4htict92X1EQ7ejDbaHDVdYA== + dependencies: + cookie "^0.4.1" + set-cookie-parser "^2.4.6" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -6266,6 +6279,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-cookie-parser@^2.4.6: + version "2.4.8" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2" + integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" From 578d0315251dbe8438706edb01a2412c5636dfda Mon Sep 17 00:00:00 2001 From: Diogo Sales Date: Fri, 18 Jun 2021 13:34:21 -0300 Subject: [PATCH 10/33] Sidebar finished and screens templates created --- src/components/ActiveLink.tsx | 37 ++++++ src/components/Input.tsx | 27 ++-- src/components/Sidebar/SidebarLink.tsx | 40 ++++-- src/components/Sidebar/SidebarNav.tsx | 24 ++-- src/components/Sidebar/SidebarProfile.tsx | 2 +- src/components/Sidebar/index.tsx | 10 +- src/pages/_app.tsx | 6 +- src/pages/_document.tsx | 2 +- src/pages/dashboard.tsx | 30 ++++- src/pages/index.tsx | 143 ++++------------------ src/pages/login.tsx | 61 +-------- src/pages/register.tsx | 31 +++++ src/pages/settings.tsx | 31 +++++ src/pages/signup.tsx | 94 +++++++------- src/pages/stock.tsx | 31 +++++ src/services/api.ts | 37 +----- 16 files changed, 298 insertions(+), 308 deletions(-) create mode 100644 src/components/ActiveLink.tsx create mode 100644 src/pages/register.tsx create mode 100644 src/pages/settings.tsx create mode 100644 src/pages/stock.tsx diff --git a/src/components/ActiveLink.tsx b/src/components/ActiveLink.tsx new file mode 100644 index 0000000..ece6c62 --- /dev/null +++ b/src/components/ActiveLink.tsx @@ -0,0 +1,37 @@ +import Link, { LinkProps } from 'next/link'; +import { useRouter } from 'next/router'; +import { cloneElement, ReactElement } from 'react'; + +interface ActiveLinkProps extends LinkProps { + children: ReactElement; + shouldMatchExactHref?: boolean; +} + +export function ActiveLink({ + children, + shouldMatchExactHref = false, + ...rest +}: ActiveLinkProps): JSX.Element { + const { asPath } = useRouter(); + + let isActive = false; + + if (shouldMatchExactHref && (asPath === rest.href || asPath === rest.as)) { + isActive = true; + } + + if ( + !shouldMatchExactHref && + (asPath.startsWith(String(rest.href)) || asPath.startsWith(String(rest.as))) + ) { + isActive = true; + } + return ( + + {cloneElement(children, { + bgColor: isActive ? 'main.white' : 'main.darkBlue', + color: isActive ? 'main.darkBlue' : 'main.white', + })} + + ); +} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 95428a1..26d700e 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -10,7 +10,6 @@ import { InputProps as ChakraInputProps, Tooltip, InputRightElement, - InputLeftElement, } from '@chakra-ui/react'; import { FiAlertCircle } from 'react-icons/fi'; @@ -19,11 +18,10 @@ interface InputProps extends ChakraInputProps { label?: string; error?: FieldError; isDark?: boolean; - leftInputElement?: string; } const InputBase: ForwardRefRenderFunction = ( - { name, isDark = false, label, error = null, leftInputElement, ...rest }, + { name, isDark = false, label, error = null, ...rest }, ref ) => { return ( @@ -34,13 +32,14 @@ const InputBase: ForwardRefRenderFunction = ( justifyContent="center" isInvalid={!!error} maxW="450px" + mb="6" > {label} @@ -50,32 +49,22 @@ const InputBase: ForwardRefRenderFunction = ( alignItems="center" justifyContent="center" > - {leftInputElement && ( - - - {leftInputElement} - - - )} - diff --git a/src/components/Sidebar/SidebarLink.tsx b/src/components/Sidebar/SidebarLink.tsx index 6ac667c..8ea4ca5 100644 --- a/src/components/Sidebar/SidebarLink.tsx +++ b/src/components/Sidebar/SidebarLink.tsx @@ -1,20 +1,42 @@ -import { Box, Link, Icon, Text, LinkProps } from '@chakra-ui/react'; +import { + Link as ChakraLink, + Icon, + Text, + LinkProps as ChakraLinkProps, +} from '@chakra-ui/react'; import { ElementType } from 'react'; +import { ActiveLink } from '../ActiveLink'; -interface SidebarLinkProps extends LinkProps { +interface SidebarLinkProps extends ChakraLinkProps { icon: ElementType; children: string; + href: string; } -export function SidebarLink({ icon, children }: SidebarLinkProps): JSX.Element { +export function SidebarLink({ + icon, + children, + href, + ...rest +}: SidebarLinkProps): JSX.Element { return ( - - - - + + + + {children} - - + + ); } diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 24c5210..41bc3c4 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -1,9 +1,9 @@ import { Stack } from '@chakra-ui/react'; import { - RiDashboardFill, - RiArchiveFill, - RiDraftFill, - RiSettings5Fill, + RiDashboardLine, + RiTBoxLine, + RiFileEditLine, + RiSettingsLine, } from 'react-icons/ri'; import { SidebarLink } from './SidebarLink'; import { SidebarProfile } from './SidebarProfile'; @@ -13,10 +13,18 @@ export function SidebarNav(): JSX.Element { - Dashboard - Estoque - Cadastro - Configurções + + Dashboard + + + Estoque + + + Cadastro + + + Configurções + ); } diff --git a/src/components/Sidebar/SidebarProfile.tsx b/src/components/Sidebar/SidebarProfile.tsx index 66eb38a..5b958be 100644 --- a/src/components/Sidebar/SidebarProfile.tsx +++ b/src/components/Sidebar/SidebarProfile.tsx @@ -7,7 +7,7 @@ interface SidebarProfileProps { export function SidebarProfile({ name }: SidebarProfileProps): JSX.Element { return ( - + {name} diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index 9a07866..f9124dd 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -23,7 +23,7 @@ export function Sidebar(): JSX.Element { return ( - + Navegação @@ -36,7 +36,13 @@ export function Sidebar(): JSX.Element { } return ( - + ); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 39f8c08..77ed584 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -3,15 +3,15 @@ import { ChakraProvider } from '@chakra-ui/react'; import { QueryClientProvider } from 'react-query'; import { theme } from '../styles/theme'; import { queryClient } from '../services/queryClient'; -import { AppProvider } from '../components/providers/AppProvider'; +import { SidebarDrawerProvider } from '../contexts/SidebarDrawerContext'; function MyApp({ Component, pageProps }: AppProps): JSX.Element { return ( - + - + ); diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index ade8a37..3cea3b7 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -7,7 +7,7 @@ export default class MyDocument extends Document { diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index d690d2a..8985c4e 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -1,11 +1,31 @@ -import { withSSRAuth } from '../utils/withSSRAuth'; +import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; +import { Sidebar } from '../components/Sidebar'; -export default function Dashboard(): JSX.Element { - return

Dashboard

; +export default function Stock(props): JSX.Element { + return ( + + + + + + + Dashboard + + + + + ); } -export const getServerSideProps = withSSRAuth(async ctx => { +export const getServerSideProps: GetServerSideProps = async () => { return { props: {}, }; -}); +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index bf6693b..89b061f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,125 +1,34 @@ -import { - Box, - Stack, - Text, - Image, - Flex, - HStack, - Button, -} from '@chakra-ui/react'; -import Link from 'next/link'; +import { Flex, Icon, IconButton, useBreakpointValue } from '@chakra-ui/react'; +import { RiMenuLine } from 'react-icons/ri'; +import { Sidebar } from '../components/Sidebar'; +import { useSidebarDrawer } from '../contexts/SidebarDrawerContext'; export default function Home(): JSX.Element { - return ( - - - - - - - + const { onOpen } = useSidebarDrawer() - - - + const isWideVersion = useBreakpointValue({ + base: false, + lg: true + }) - - - + return ( + + { !isWideVersion && ( + } + fontSize='28' + variant='unstyled' + onClick={onOpen} + mr='2' + > - - - - + + )} - - - - - + + - - - Título ou texto chamativo - - - Paraágrafo que acompanha o título out texto chamativo. blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla - - - - ); + + ) } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index f0144b7..fcee357 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,49 +1,8 @@ -import { Grid, Image, Flex, Box, VStack, useToast } from '@chakra-ui/react'; -import { useContext } from 'react'; -import { useForm } from 'react-hook-form'; -import { useMutation } from 'react-query'; +import { Grid, Image, Flex, Box } from '@chakra-ui/react'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; -import { AuthContext } from '../contexts/AuthContext'; - -interface SignInFormData { - login: string; - password: string; -} export default function Login(): JSX.Element { - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); - - const toast = useToast(); - - const { signIn } = useContext(AuthContext); - - const authenticate = useMutation( - async ({ login, password }: SignInFormData): Promise => { - await signIn({ login, password }); - } - ); - - const onSubmit = async ({ - login, - password, - }: SignInFormData): Promise => { - try { - await authenticate.mutateAsync({ login, password }); - } catch (err) { - toast({ - duration: 3000, - status: 'error', - title: 'Algo deu errado', - description: 'Usuário/senha incorretos!', - position: 'top-right', - }); - } - }; return ( - - - - - + + + ); diff --git a/src/pages/register.tsx b/src/pages/register.tsx new file mode 100644 index 0000000..7d07c1d --- /dev/null +++ b/src/pages/register.tsx @@ -0,0 +1,31 @@ +import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; +import { Sidebar } from '../components/Sidebar'; + +export default function Stock(props): JSX.Element { + return ( + + + + + + + Cadastro + + + + + ); +} + +export const getServerSideProps: GetServerSideProps = async () => { + return { + props: {}, + }; +}; diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx new file mode 100644 index 0000000..ea609dc --- /dev/null +++ b/src/pages/settings.tsx @@ -0,0 +1,31 @@ +import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; +import { Sidebar } from '../components/Sidebar'; + +export default function Stock(props): JSX.Element { + return ( + + + + + + + Configurações + + + + + ); +} + +export const getServerSideProps: GetServerSideProps = async () => { + return { + props: {}, + }; +}; diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index af9ddb0..2275431 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -1,4 +1,4 @@ -import { Grid, Box, Flex, Image, useToast, VStack } from '@chakra-ui/react'; +import { Grid, Box, Flex, Image, useToast } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; import { useRouter } from 'next/router'; @@ -6,7 +6,7 @@ import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; import { Input } from '../components/Input'; import { Button } from '../components/Button'; -import { api } from '../services/apiClient'; +import { api } from '../services/api'; import { queryClient } from '../services/queryClient'; interface ICreateUserFormData { @@ -77,10 +77,7 @@ export default function SignUp(): JSX.Element { const onSubmit = async (user: ICreateUserFormData): Promise => { try { - await createUser.mutateAsync({ - ...user, - phone: `+55${user.phone}`, - }); + await createUser.mutateAsync(user); toast({ duration: 3000, @@ -115,53 +112,44 @@ export default function SignUp(): JSX.Element { onSubmit={handleSubmit(onSubmit, onError)} > - - - - - - - - + + + + + + diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx new file mode 100644 index 0000000..aa08f79 --- /dev/null +++ b/src/pages/stock.tsx @@ -0,0 +1,31 @@ +import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; +import { Sidebar } from '../components/Sidebar'; + +export default function Stock(props): JSX.Element { + return ( + + + + + + + Estoque + + + + + ); +} + +export const getServerSideProps: GetServerSideProps = async () => { + return { + props: {}, + }; +}; diff --git a/src/services/api.ts b/src/services/api.ts index 747760e..02ac6a7 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,34 +1,5 @@ -import axios, { AxiosError, AxiosInstance } from 'axios'; -import { parseCookies, setCookie } from 'nookies'; -import { signOut } from '../contexts/AuthContext'; -import { AuthTokenError } from './errors/AuthTokenError'; +import axios from 'axios'; -const failedRequestsQueue = []; - -export function setupApiClient(ctx = undefined): AxiosInstance { - const cookies = parseCookies(ctx); - - const api = axios.create({ - baseURL: 'http://localhost:3333', - headers: { - Authorization: `Bearer ${cookies['@openwms.token']}`, - }, - }); - - api.interceptors.response.use( - response => { - return response; - }, - (error: AxiosError) => { - if (process.browser) { - signOut(); - } else { - return Promise.reject(new AuthTokenError()); - } - - return Promise.reject(error); - } - ); - - return api; -} +export const api = axios.create({ + baseURL: 'http://localhost:3333/', +}); From f617b1062f64e0cd6fa6aab287482abce3fa6160 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 15:16:00 -0400 Subject: [PATCH 11/33] Fix colorScheme for button and Input --- src/components/Button.tsx | 6 ++---- src/components/Input.tsx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 2ce939e..a0ca591 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -14,10 +14,8 @@ export function Button({ children, ...rest }: ButtonProps): JSX.Element { w="100%" borderRadius="20px" h="60px" - bg="main.green" - _hover={{ - bg: 'main.lightGreen', - }} + colorScheme="greenBtn" + color="main.darkBlue" {...rest} > {children} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 95428a1..da701e7 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -68,8 +68,8 @@ const InputBase: ForwardRefRenderFunction = ( borderWidth="3px" borderColor="transparent" bgColor="main.offWhite" - _placeholder={{ - color: 'main.darkBlue', + _active={{ + backgroundColor: '#ff3535', }} _hover={{ borderColor: isDark ? 'main.green' : 'main.darkBlue', From b8f556eaa8b8cf0200afa7e828903cb32ad0fb49 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 15:17:13 -0400 Subject: [PATCH 12/33] Add Color Scheme to buttons and Refactoring Home page --- src/pages/index.tsx | 200 ++++++++++++++++++++++++------------------- src/pages/login.tsx | 2 +- src/pages/signup.tsx | 8 +- src/styles/theme.ts | 7 ++ 4 files changed, 126 insertions(+), 91 deletions(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index bf6693b..676a875 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -6,6 +6,7 @@ import { Flex, HStack, Button, + Link as ChakraLink, } from '@chakra-ui/react'; import Link from 'next/link'; @@ -13,113 +14,134 @@ export default function Home(): JSX.Element { return ( - - - - - - - - - - - - - - - - - + + + + + + + + + + RECURSOS + - - + SERVIÇOS + + + + + + Login + + + + + + Cadastre-se + + + - + + + + Um novo{' '} + + W + + + M + + + S + {' '} + para simplificar a sua gestão. + + + O openWMS foi criado pensando em facilitar a vida + das pessoas e dos micro e pequenos empresários, tornando a gestão + mais simples e mais acessível para todo e qualquer item que você + tenha comprado.{' '} + + + clique aqui + + {' '} + e faça seu cadastro agora e faça parte dessa revolução. + + - - - Título ou texto chamativo - - - Paraágrafo que acompanha o título out texto chamativo. blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla blablabla - blablabla blablabla blablablabla bla blabla blablavbla - - ); } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index f0144b7..79b6666 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -61,7 +61,7 @@ export default function Login(): JSX.Element { as="form" onSubmit={handleSubmit(onSubmit)} > - + - + @@ -141,6 +144,7 @@ export default function SignUp(): JSX.Element { isDark name="phone" leftInputElement="🇧🇷" + placeholder="XX988776655" label="Número de telefone" error={errors.phone} {...register('phone')} @@ -150,6 +154,7 @@ export default function SignUp(): JSX.Element { name="password" label="Senha" type="password" + placeholder="Senha de no mínimo 6 digitos" error={errors.password} {...register('password')} /> @@ -157,6 +162,7 @@ export default function SignUp(): JSX.Element { isDark name="passwordConfirmation" label="Confirmação de Senha" + placeholder="Confirme sua senha igual a anterior" type="password" error={errors.passwordConfirmation} {...register('passwordConfirmation')} diff --git a/src/styles/theme.ts b/src/styles/theme.ts index b63fe83..ad9136d 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -4,16 +4,23 @@ export const theme = extendTheme({ colors: { main: { darkBlue: '#023047', + darkBlue80: '#305467', violet: '#54478C', blue: '#219EBC', cyan: '#8ECAE6', orange: '#FB8500', yellow: '#FFB703', + yellow80: '#FAC030', green: '#16DB93', lightGreen: '#16DB93', white: '#F5F5F5', offWhite: '#EEEEEE', }, + greenBtn: { + 500: '#16DB93', + 600: '#14C584', + 700: '#12AF76', + }, }, fonts: { heading: 'Quicksand', From 43d293e6e73a598cb06796908924b41a9a2418a8 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 15:17:33 -0400 Subject: [PATCH 13/33] Add new logo in Horizontal and vertical mode. --- public/images/logoHorizontal.svg | 9 +++++++++ public/images/logoVertical.svg | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 public/images/logoHorizontal.svg create mode 100644 public/images/logoVertical.svg diff --git a/public/images/logoHorizontal.svg b/public/images/logoHorizontal.svg new file mode 100644 index 0000000..b1bcf70 --- /dev/null +++ b/public/images/logoHorizontal.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/logoVertical.svg b/public/images/logoVertical.svg new file mode 100644 index 0000000..d8dc928 --- /dev/null +++ b/public/images/logoVertical.svg @@ -0,0 +1,9 @@ + + + + + + + + + From 8ba9de5de9c3086bbc55383290a8dd55cff13e52 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 15:53:01 -0400 Subject: [PATCH 14/33] Created dashboard base, and relocated providers fold --- src/components/Input.tsx | 3 --- src/pages/_app.tsx | 2 +- src/pages/dashboard.tsx | 14 +++++++++++++- src/{components => }/providers/AppProvider.tsx | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) rename src/{components => }/providers/AppProvider.tsx (65%) diff --git a/src/components/Input.tsx b/src/components/Input.tsx index da701e7..1c36c00 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -68,9 +68,6 @@ const InputBase: ForwardRefRenderFunction = ( borderWidth="3px" borderColor="transparent" bgColor="main.offWhite" - _active={{ - backgroundColor: '#ff3535', - }} _hover={{ borderColor: isDark ? 'main.green' : 'main.darkBlue', }} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 39f8c08..fa6eac3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -3,7 +3,7 @@ import { ChakraProvider } from '@chakra-ui/react'; import { QueryClientProvider } from 'react-query'; import { theme } from '../styles/theme'; import { queryClient } from '../services/queryClient'; -import { AppProvider } from '../components/providers/AppProvider'; +import { AppProvider } from '../providers/AppProvider'; function MyApp({ Component, pageProps }: AppProps): JSX.Element { return ( diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index d690d2a..76a29fe 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -1,7 +1,19 @@ +import { Flex, Heading } from '@chakra-ui/react'; + +import { Sidebar } from '../components/Sidebar'; import { withSSRAuth } from '../utils/withSSRAuth'; export default function Dashboard(): JSX.Element { - return

Dashboard

; + return ( + + + + + Dashboard + + + + ); } export const getServerSideProps = withSSRAuth(async ctx => { diff --git a/src/components/providers/AppProvider.tsx b/src/providers/AppProvider.tsx similarity index 65% rename from src/components/providers/AppProvider.tsx rename to src/providers/AppProvider.tsx index 3d3ec43..7cf9e6a 100644 --- a/src/components/providers/AppProvider.tsx +++ b/src/providers/AppProvider.tsx @@ -1,5 +1,5 @@ -import { AuthProvider } from '../../contexts/AuthContext'; -import { SidebarDrawerProvider } from '../../contexts/SidebarDrawerContext'; +import { AuthProvider } from '../contexts/AuthContext'; +import { SidebarDrawerProvider } from '../contexts/SidebarDrawerContext'; interface AppProviderProps { children: React.ReactElement; From b9714f7dd0f35279fc7e7a372f413aa5175bc559 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 15:59:24 -0400 Subject: [PATCH 15/33] Fix: warehouse page and getServerSide Types --- src/pages/dashboard.tsx | 3 ++- src/pages/products.tsx | 3 ++- src/pages/warehouse.tsx | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/pages/warehouse.tsx diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 76a29fe..bf0c652 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -1,4 +1,5 @@ import { Flex, Heading } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; import { Sidebar } from '../components/Sidebar'; import { withSSRAuth } from '../utils/withSSRAuth'; @@ -16,7 +17,7 @@ export default function Dashboard(): JSX.Element { ); } -export const getServerSideProps = withSSRAuth(async ctx => { +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { return { props: {}, }; diff --git a/src/pages/products.tsx b/src/pages/products.tsx index ee4a492..b0d100c 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -13,6 +13,7 @@ import { Image, Box, } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; import { ModalRegisterItem } from '../components/ModalRegisterItem'; import { Button } from '../components/Button'; import { Sidebar } from '../components/Sidebar'; @@ -238,7 +239,7 @@ export default function Products(): JSX.Element { ); } -export const getServerSideProps = withSSRAuth(async ctx => { +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { return { props: {}, }; diff --git a/src/pages/warehouse.tsx b/src/pages/warehouse.tsx new file mode 100644 index 0000000..b563981 --- /dev/null +++ b/src/pages/warehouse.tsx @@ -0,0 +1,32 @@ +import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; +import { Sidebar } from '../components/Sidebar'; +import { withSSRAuth } from '../utils/withSSRAuth'; + +export default function Warehouse(): JSX.Element { + return ( + + + + + + + Estoque + + + + + ); +} + +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { + return { + props: {}, + }; +}); From d950c6770edfcea42b77af950c0b7a411c4edb1a Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 16:42:12 -0400 Subject: [PATCH 16/33] refactoring --- src/components/ActiveLink.tsx | 37 ++ src/components/Sidebar/SidebarLink.tsx | 40 ++- src/components/Sidebar/SidebarNav.tsx | 16 +- src/components/Sidebar/SidebarProfile.tsx | 39 +- src/components/Sidebar/index.tsx | 7 +- src/contexts/AuthContext.tsx | 31 +- src/contexts/SidebarDrawerContext.tsx | 11 +- src/hooks/useAuth.tsx | 6 + src/hooks/useSidebarDrawer.tsx | 8 + src/pages/dashboard.tsx | 2 +- src/pages/login.tsx | 5 +- src/pages/products.tsx | 416 +++++++++++----------- src/pages/settings.tsx | 24 ++ src/pages/warehouse.tsx | 18 +- src/services/api.ts | 5 +- src/services/apiClient.ts | 1 + 16 files changed, 394 insertions(+), 272 deletions(-) create mode 100644 src/components/ActiveLink.tsx create mode 100644 src/hooks/useAuth.tsx create mode 100644 src/hooks/useSidebarDrawer.tsx create mode 100644 src/pages/settings.tsx diff --git a/src/components/ActiveLink.tsx b/src/components/ActiveLink.tsx new file mode 100644 index 0000000..ece6c62 --- /dev/null +++ b/src/components/ActiveLink.tsx @@ -0,0 +1,37 @@ +import Link, { LinkProps } from 'next/link'; +import { useRouter } from 'next/router'; +import { cloneElement, ReactElement } from 'react'; + +interface ActiveLinkProps extends LinkProps { + children: ReactElement; + shouldMatchExactHref?: boolean; +} + +export function ActiveLink({ + children, + shouldMatchExactHref = false, + ...rest +}: ActiveLinkProps): JSX.Element { + const { asPath } = useRouter(); + + let isActive = false; + + if (shouldMatchExactHref && (asPath === rest.href || asPath === rest.as)) { + isActive = true; + } + + if ( + !shouldMatchExactHref && + (asPath.startsWith(String(rest.href)) || asPath.startsWith(String(rest.as))) + ) { + isActive = true; + } + return ( + + {cloneElement(children, { + bgColor: isActive ? 'main.white' : 'main.darkBlue', + color: isActive ? 'main.darkBlue' : 'main.white', + })} + + ); +} diff --git a/src/components/Sidebar/SidebarLink.tsx b/src/components/Sidebar/SidebarLink.tsx index 6ac667c..8ea4ca5 100644 --- a/src/components/Sidebar/SidebarLink.tsx +++ b/src/components/Sidebar/SidebarLink.tsx @@ -1,20 +1,42 @@ -import { Box, Link, Icon, Text, LinkProps } from '@chakra-ui/react'; +import { + Link as ChakraLink, + Icon, + Text, + LinkProps as ChakraLinkProps, +} from '@chakra-ui/react'; import { ElementType } from 'react'; +import { ActiveLink } from '../ActiveLink'; -interface SidebarLinkProps extends LinkProps { +interface SidebarLinkProps extends ChakraLinkProps { icon: ElementType; children: string; + href: string; } -export function SidebarLink({ icon, children }: SidebarLinkProps): JSX.Element { +export function SidebarLink({ + icon, + children, + href, + ...rest +}: SidebarLinkProps): JSX.Element { return ( - - - - + + + + {children} - - + + ); } diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 24c5210..65ddaef 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -13,10 +13,18 @@ export function SidebarNav(): JSX.Element { - Dashboard - Estoque - Cadastro - Configurções + + Dashboard + + + Estoque + + + Cadastro + + + Configurções + ); } diff --git a/src/components/Sidebar/SidebarProfile.tsx b/src/components/Sidebar/SidebarProfile.tsx index 66eb38a..d69a16e 100644 --- a/src/components/Sidebar/SidebarProfile.tsx +++ b/src/components/Sidebar/SidebarProfile.tsx @@ -1,16 +1,45 @@ -import { Box, Avatar, Text } from '@chakra-ui/react'; +import { Flex, Avatar, Text, IconButton, Icon } from '@chakra-ui/react'; +import { RiShutDownLine } from 'react-icons/ri'; +import { useAuth } from '../../hooks/useAuth'; interface SidebarProfileProps { name: string; } export function SidebarProfile({ name }: SidebarProfileProps): JSX.Element { + const { signOut } = useAuth(); return ( - - - + + + {name} - + + + + + ); } diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index 9a07866..0b7a4a2 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -8,7 +8,8 @@ import { DrawerOverlay, useBreakpointValue, } from '@chakra-ui/react'; -import { useSidebarDrawer } from '../../contexts/SidebarDrawerContext'; +import { useSidebarDrawer } from '../../hooks/useSidebarDrawer'; + import { SidebarNav } from './SidebarNav'; export function Sidebar(): JSX.Element { @@ -23,7 +24,7 @@ export function Sidebar(): JSX.Element { return ( - + Navegação @@ -36,7 +37,7 @@ export function Sidebar(): JSX.Element { } return ( - + ); diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index a79ec08..be5fe7b 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,5 +1,5 @@ import { createContext, ReactNode, useEffect, useState } from 'react'; -import { setCookie, parseCookies, destroyCookie } from 'nookies'; +import { setCookie, destroyCookie } from 'nookies'; import Router from 'next/router'; // eslint-disable-next-line import/no-cycle import { api } from '../services/apiClient'; @@ -17,7 +17,7 @@ type SignInCredentials = { password: string; }; -type AuthContextData = { +export type AuthContextData = { signIn(credentials: SignInCredentials): Promise; signOut: () => void; user: User; @@ -28,11 +28,11 @@ type AuthProviderProps = { children: ReactNode; }; -export const AuthContext = createContext({} as AuthContextData); +const AuthContext = createContext({} as AuthContextData); let authChannel: BroadcastChannel; -export function signOut(): void { +function signOut(): void { destroyCookie(undefined, '@openwms.token'); authChannel.postMessage('signOut'); @@ -40,7 +40,7 @@ export function signOut(): void { Router.push('/'); } -export function AuthProvider({ children }: AuthProviderProps): JSX.Element { +function AuthProvider({ children }: AuthProviderProps): JSX.Element { const [user, setUser] = useState(null); const isAuthenticated = !!user; @@ -61,23 +61,6 @@ export function AuthProvider({ children }: AuthProviderProps): JSX.Element { }; }, []); - // useEffect(() => { - // const { '@openwms.token': token } = parseCookies(); - - // if (token) { - // api - // .get('/me') - // .then(response => { - // const { email, permissions, roles } = response.data; - - // setUser({ email, permissions, roles }); - // }) - // .catch(() => { - // signOut(); - // }); - // } - // }, []); - async function signIn({ login, password }: SignInCredentials): Promise { try { const response = await api.post('sessions', { @@ -85,8 +68,6 @@ export function AuthProvider({ children }: AuthProviderProps): JSX.Element { password, }); - console.log(response); - const { token, user: userData } = response.data; setCookie(undefined, '@openwms.token', token, { @@ -111,3 +92,5 @@ export function AuthProvider({ children }: AuthProviderProps): JSX.Element { ); } + +export { AuthContext, signOut, AuthProvider }; diff --git a/src/contexts/SidebarDrawerContext.tsx b/src/contexts/SidebarDrawerContext.tsx index bef9d91..3c32798 100644 --- a/src/contexts/SidebarDrawerContext.tsx +++ b/src/contexts/SidebarDrawerContext.tsx @@ -1,20 +1,18 @@ import { useDisclosure, UseDisclosureReturn } from '@chakra-ui/react'; -import { useRouter } from 'next/router'; -import { createContext, ReactNode, useContext, useEffect } from 'react'; +import { createContext, ReactNode, useEffect } from 'react'; interface SidebarDrawerProviderProps { children: ReactNode; } -type SidebarDrawerContextData = UseDisclosureReturn; +export type SidebarDrawerContextData = UseDisclosureReturn; const SidebarDrawerContext = createContext({} as SidebarDrawerContextData); -export function SidebarDrawerProvider({ +function SidebarDrawerProvider({ children, }: SidebarDrawerProviderProps): JSX.Element { const disclosure = useDisclosure(); - const router = useRouter(); useEffect(() => { disclosure.onClose(); @@ -27,5 +25,4 @@ export function SidebarDrawerProvider({ ); } -export const useSidebarDrawer = (): SidebarDrawerContextData => - useContext(SidebarDrawerContext); +export { SidebarDrawerProvider, SidebarDrawerContext }; diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx new file mode 100644 index 0000000..72bacdc --- /dev/null +++ b/src/hooks/useAuth.tsx @@ -0,0 +1,6 @@ +import { useContext } from 'react'; +import { AuthContext, AuthContextData } from '../contexts/AuthContext'; + +const useAuth = (): AuthContextData => useContext(AuthContext); + +export { useAuth }; diff --git a/src/hooks/useSidebarDrawer.tsx b/src/hooks/useSidebarDrawer.tsx new file mode 100644 index 0000000..079eb56 --- /dev/null +++ b/src/hooks/useSidebarDrawer.tsx @@ -0,0 +1,8 @@ +import { useContext } from 'react'; +import { + SidebarDrawerContextData, + SidebarDrawerContext, +} from '../contexts/SidebarDrawerContext'; + +export const useSidebarDrawer = (): SidebarDrawerContextData => + useContext(SidebarDrawerContext); diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index bf0c652..ee6b51a 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -8,7 +8,7 @@ export default function Dashboard(): JSX.Element { return ( - + Dashboard diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 79b6666..be84d58 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,10 +1,9 @@ import { Grid, Image, Flex, Box, VStack, useToast } from '@chakra-ui/react'; -import { useContext } from 'react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; -import { AuthContext } from '../contexts/AuthContext'; +import { useAuth } from '../hooks/useAuth'; interface SignInFormData { login: string; @@ -20,7 +19,7 @@ export default function Login(): JSX.Element { const toast = useToast(); - const { signIn } = useContext(AuthContext); + const { signIn } = useAuth(); const authenticate = useMutation( async ({ login, password }: SignInFormData): Promise => { diff --git a/src/pages/products.tsx b/src/pages/products.tsx index b0d100c..9ac65c3 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -24,215 +24,217 @@ export default function Products(): JSX.Element { return ( - - - - Cadastro de produtos - - - - - Lista de produtos - - + + + + + Cadastro de produtos - + + + + Lista de produtos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CódigoNomeCategoriaTipoDataQtdeUndValorCriado em
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CódigoNomeCategoriaTipoDataQtdeUndValorCriado em
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
-
diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx new file mode 100644 index 0000000..156e311 --- /dev/null +++ b/src/pages/settings.tsx @@ -0,0 +1,24 @@ +import { Flex, Heading } from '@chakra-ui/react'; +import { GetServerSideProps } from 'next'; + +import { Sidebar } from '../components/Sidebar'; +import { withSSRAuth } from '../utils/withSSRAuth'; + +export default function Settings(): JSX.Element { + return ( + + + + + Configurações + + + + ); +} + +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { + return { + props: {}, + }; +}); diff --git a/src/pages/warehouse.tsx b/src/pages/warehouse.tsx index b563981..cccf08d 100644 --- a/src/pages/warehouse.tsx +++ b/src/pages/warehouse.tsx @@ -5,11 +5,17 @@ import { withSSRAuth } from '../utils/withSSRAuth'; export default function Warehouse(): JSX.Element { return ( - + - - - + + + Estoque - +
- +
); } diff --git a/src/services/api.ts b/src/services/api.ts index 747760e..bb58ef8 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,10 +1,9 @@ import axios, { AxiosError, AxiosInstance } from 'axios'; -import { parseCookies, setCookie } from 'nookies'; +import { parseCookies } from 'nookies'; +// eslint-disable-next-line import/no-cycle import { signOut } from '../contexts/AuthContext'; import { AuthTokenError } from './errors/AuthTokenError'; -const failedRequestsQueue = []; - export function setupApiClient(ctx = undefined): AxiosInstance { const cookies = parseCookies(ctx); diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts index 2a39f99..a97062e 100644 --- a/src/services/apiClient.ts +++ b/src/services/apiClient.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-cycle import { setupApiClient } from './api'; export const api = setupApiClient(); From 5e7c497313aeddb4ce6c5ecf860390d41e8979e5 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 17:03:40 -0400 Subject: [PATCH 17/33] Fix merge problems --- src/components/Input.tsx | 23 ++++++++++++++------ src/components/Sidebar/SidebarNav.tsx | 8 +++---- src/contexts/AuthContext.tsx | 10 ++++++++- src/pages/_app.tsx | 4 ++-- src/pages/dashboard.tsx | 2 +- src/pages/login.tsx | 2 ++ src/pages/register.tsx | 31 --------------------------- src/pages/signup.tsx | 4 ++-- 8 files changed, 37 insertions(+), 47 deletions(-) delete mode 100644 src/pages/register.tsx diff --git a/src/components/Input.tsx b/src/components/Input.tsx index d396f5a..1c36c00 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -10,6 +10,7 @@ import { InputProps as ChakraInputProps, Tooltip, InputRightElement, + InputLeftElement, } from '@chakra-ui/react'; import { FiAlertCircle } from 'react-icons/fi'; @@ -18,10 +19,11 @@ interface InputProps extends ChakraInputProps { label?: string; error?: FieldError; isDark?: boolean; + leftInputElement?: string; } const InputBase: ForwardRefRenderFunction = ( - { name, isDark = false, label, error = null, ...rest }, + { name, isDark = false, label, error = null, leftInputElement, ...rest }, ref ) => { return ( @@ -32,14 +34,13 @@ const InputBase: ForwardRefRenderFunction = ( justifyContent="center" isInvalid={!!error} maxW="450px" - mb="6" > {label} @@ -49,19 +50,29 @@ const InputBase: ForwardRefRenderFunction = ( alignItems="center" justifyContent="center" > + {leftInputElement && ( + + + {leftInputElement} + + + )} + diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 7eaf01f..65ddaef 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -1,9 +1,9 @@ import { Stack } from '@chakra-ui/react'; import { - RiDashboardLine, - RiTBoxLine, - RiFileEditLine, - RiSettingsLine, + RiDashboardFill, + RiArchiveFill, + RiDraftFill, + RiSettings5Fill, } from 'react-icons/ri'; import { SidebarLink } from './SidebarLink'; import { SidebarProfile } from './SidebarProfile'; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index be5fe7b..4283b14 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -2,6 +2,8 @@ import { createContext, ReactNode, useEffect, useState } from 'react'; import { setCookie, destroyCookie } from 'nookies'; import Router from 'next/router'; // eslint-disable-next-line import/no-cycle +import { useToast } from '@chakra-ui/react'; +// eslint-disable-next-line import/no-cycle import { api } from '../services/apiClient'; type User = { @@ -44,6 +46,8 @@ function AuthProvider({ children }: AuthProviderProps): JSX.Element { const [user, setUser] = useState(null); const isAuthenticated = !!user; + const toast = useToast(); + useEffect(() => { authChannel = new BroadcastChannel('auth'); @@ -82,7 +86,11 @@ function AuthProvider({ children }: AuthProviderProps): JSX.Element { Router.push('/dashboard'); authChannel.postMessage('signIn'); } catch (err) { - console.log(err); + toast({ + status: 'error', + title: err.response.data.error, + position: 'top-right', + }); } } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index af27df4..fa6eac3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -9,9 +9,9 @@ function MyApp({ Component, pageProps }: AppProps): JSX.Element { return ( - + - + ); diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 93a8a26..ee6b51a 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -21,4 +21,4 @@ export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { return { props: {}, }; -}; +}); diff --git a/src/pages/login.tsx b/src/pages/login.tsx index a2c7a5d..be84d58 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -57,6 +57,8 @@ export default function Login(): JSX.Element { flexDir="column" alignItems="center" justifyContent="center" + as="form" + onSubmit={handleSubmit(onSubmit)} > diff --git a/src/pages/register.tsx b/src/pages/register.tsx deleted file mode 100644 index 7d07c1d..0000000 --- a/src/pages/register.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Flex, Text, HStack, Img } from '@chakra-ui/react'; -import { GetServerSideProps } from 'next'; -import { Sidebar } from '../components/Sidebar'; - -export default function Stock(props): JSX.Element { - return ( - - - - - - - Cadastro - - - - - ); -} - -export const getServerSideProps: GetServerSideProps = async () => { - return { - props: {}, - }; -}; diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index d8aa69d..8aaf5ce 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -1,4 +1,4 @@ -import { Grid, Box, Flex, Image, useToast } from '@chakra-ui/react'; +import { Grid, Box, Flex, Image, useToast, VStack } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; import { useRouter } from 'next/router'; @@ -6,7 +6,7 @@ import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; import { Input } from '../components/Input'; import { Button } from '../components/Button'; -import { api } from '../services/api'; +import { api } from '../services/apiClient'; import { queryClient } from '../services/queryClient'; interface ICreateUserFormData { From f1e7c486a4b6174c2141fffec210fa03b844c067 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Fri, 18 Jun 2021 17:13:27 -0400 Subject: [PATCH 18/33] use login to show username in sidebar --- src/components/Sidebar/SidebarNav.tsx | 2 +- src/components/Sidebar/SidebarProfile.tsx | 6 +++--- src/contexts/AuthContext.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 65ddaef..7e787a8 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -11,7 +11,7 @@ import { SidebarProfile } from './SidebarProfile'; export function SidebarNav(): JSX.Element { return ( - + Dashboard diff --git a/src/components/Sidebar/SidebarProfile.tsx b/src/components/Sidebar/SidebarProfile.tsx index d69a16e..cd8c87f 100644 --- a/src/components/Sidebar/SidebarProfile.tsx +++ b/src/components/Sidebar/SidebarProfile.tsx @@ -6,8 +6,8 @@ interface SidebarProfileProps { name: string; } -export function SidebarProfile({ name }: SidebarProfileProps): JSX.Element { - const { signOut } = useAuth(); +export function SidebarProfile(): JSX.Element { + const { signOut, user } = useAuth(); return ( - {name} + {user.login} Date: Sat, 19 Jun 2021 10:42:45 -0400 Subject: [PATCH 19/33] add user data to localStorage and refactoring stock, login and signup --- src/components/Sidebar/SidebarNav.tsx | 2 +- src/components/Sidebar/SidebarProfile.tsx | 6 +--- src/contexts/AuthContext.tsx | 11 +++++++ src/pages/login.tsx | 9 +++++- src/pages/signup.tsx | 1 + src/pages/stock.tsx | 25 +++++++++------ src/pages/warehouse.tsx | 38 ----------------------- 7 files changed, 38 insertions(+), 54 deletions(-) delete mode 100644 src/pages/warehouse.tsx diff --git a/src/components/Sidebar/SidebarNav.tsx b/src/components/Sidebar/SidebarNav.tsx index 7e787a8..a8b407e 100644 --- a/src/components/Sidebar/SidebarNav.tsx +++ b/src/components/Sidebar/SidebarNav.tsx @@ -16,7 +16,7 @@ export function SidebarNav(): JSX.Element { Dashboard - + Estoque diff --git a/src/components/Sidebar/SidebarProfile.tsx b/src/components/Sidebar/SidebarProfile.tsx index cd8c87f..219ffee 100644 --- a/src/components/Sidebar/SidebarProfile.tsx +++ b/src/components/Sidebar/SidebarProfile.tsx @@ -2,10 +2,6 @@ import { Flex, Avatar, Text, IconButton, Icon } from '@chakra-ui/react'; import { RiShutDownLine } from 'react-icons/ri'; import { useAuth } from '../../hooks/useAuth'; -interface SidebarProfileProps { - name: string; -} - export function SidebarProfile(): JSX.Element { const { signOut, user } = useAuth(); return ( @@ -25,7 +21,7 @@ export function SidebarProfile(): JSX.Element { bgColor="#F5F5F5" /> - {user.login} + {user?.login} { + const getUser = localStorage.getItem('@openwms.user'); + + if (getUser) { + setUser(JSON.parse(getUser)); + } + }, []); + useEffect(() => { authChannel = new BroadcastChannel('auth'); @@ -81,6 +90,8 @@ function AuthProvider({ children }: AuthProviderProps): JSX.Element { setUser(userData as User); + localStorage.setItem('@openwms.user', JSON.stringify(userData)); + api.defaults.headers.Authorization = `Bearer ${token}`; Router.push('/dashboard'); diff --git a/src/pages/login.tsx b/src/pages/login.tsx index be84d58..14708cd 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -62,10 +62,17 @@ export default function Login(): JSX.Element { > - + + - - - + + + Estoque - + - + ); } -export const getServerSideProps: GetServerSideProps = async () => { +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { return { props: {}, }; -}; +}); diff --git a/src/pages/warehouse.tsx b/src/pages/warehouse.tsx deleted file mode 100644 index cccf08d..0000000 --- a/src/pages/warehouse.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Flex, Text, HStack, Img } from '@chakra-ui/react'; -import { GetServerSideProps } from 'next'; -import { Sidebar } from '../components/Sidebar'; -import { withSSRAuth } from '../utils/withSSRAuth'; - -export default function Warehouse(): JSX.Element { - return ( - - - - - - - Estoque - - - - - ); -} - -export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { - return { - props: {}, - }; -}); From ae1fef677462acf5202b333932c6918901674b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeandroGon=C3=A7alves?= Date: Sat, 19 Jun 2021 12:10:16 -0300 Subject: [PATCH 20/33] add page settings --- src/pages/settings.tsx | 278 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 276 insertions(+), 2 deletions(-) diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 156e311..1126c49 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -1,17 +1,291 @@ -import { Flex, Heading } from '@chakra-ui/react'; +import { + Flex, + Heading, + Box, + Text, + Stack, + useToast, + Button as ChakraButton, +} from '@chakra-ui/react'; import { GetServerSideProps } from 'next'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import * as yup from 'yup'; +import { useState } from 'react'; +import { Input } from '../components/Input'; import { Sidebar } from '../components/Sidebar'; +import { Button } from '../components/Button'; import { withSSRAuth } from '../utils/withSSRAuth'; +import { useAuth } from '../hooks/useAuth'; +import { api } from '../services/apiClient'; + +interface RegisterFormData { + name: string; + email: string; +} +interface PasswordFormData { + oldPassword: string; + newPassword: string; + newPasswordConfirm: string; +} + +const registerSchema = yup.object().shape({ + name: yup.string(), + email: yup.string(), +}); + +const passwordSchema = yup.object().shape({ + oldPassword: yup.string().required('A senha antiga é obrigatório!'), + newPassword: yup.string().required('A nova senha é obrigatório!'), + newPasswordConfirm: yup + .string() + .required('A confirmação de senha é obrigatória!') + .oneOf([yup.ref('newPassword'), null], 'As senhas não batem!'), +}); export default function Settings(): JSX.Element { + const toast = useToast(); + const { user } = useAuth(); + + const [notifyEmail, setNotifyEmail] = useState(false); + const [notifySms, setNotifySms] = useState(false); + + const { + handleSubmit, + register, + formState: { errors }, + } = useForm({ + resolver: yupResolver(registerSchema), + defaultValues: { name: user?.name || '', email: user?.email || '' }, + }); + + const { + handleSubmit: handleSubmitPassword, + register: registerPassword, + formState: { errors: errorsPassword }, + } = useForm({ resolver: yupResolver(passwordSchema) }); + + const handleChangeRegister = async ({ + name, + email, + }: RegisterFormData): Promise => { + try { + // await api.put(`users/${user.id}`, { + // name, + // email, + // }); + toast({ + duration: 3000, + status: 'success', + title: 'Sucesso!', + description: 'nome e/ou email alterados!', + position: 'top-right', + }); + } catch (err) { + toast({ + duration: 3000, + status: 'error', + title: 'Algo deu errado', + description: 'Usuário/senha incorretos!', + position: 'top-right', + }); + } + }; + + const handleChangePassword = async ({ + oldPassword, + newPassword, + newPasswordConfirm, + }: PasswordFormData): Promise => { + try { + console.log({ oldPassword, newPassword, newPasswordConfirm }); + } catch (err) { + toast({ + duration: 3000, + status: 'error', + title: 'Algo deu errado', + description: 'Usuário/senha incorretos!', + position: 'top-right', + }); + } + }; + + const handleAlerts = async (event): Promise => { + try { + event.preventDefault(); + console.log({ notifyEmail, notifySms }); + } catch (err) { + toast({ + duration: 3000, + status: 'error', + title: 'Algo deu errado', + description: 'Usuário/senha incorretos!', + position: 'top-right', + }); + } + }; + return ( - + Configurações + + + + Perfil + + + + + + + + + + + + + + + + + + + + Notificações + + + + + Ativar alertas por email: + setNotifyEmail(!notifyEmail)} + > + {notifyEmail && ( + + )} + + + + Ativar alertas por sms: + setNotifySms(!notifySms)} + > + {notifySms && ( + + )} + + + + + + + + + ); From a72731d1f57db1b62572bbe39fada92dde452b18 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sat, 19 Jun 2021 11:30:48 -0400 Subject: [PATCH 21/33] fix: auth behavior --- src/components/ModalRegisterItem.tsx | 22 +++++++++++++++++++++- src/pages/stock.tsx | 3 +++ src/services/api.ts | 12 ++++++++---- src/utils/withSSRAuth.ts | 10 +++------- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/components/ModalRegisterItem.tsx b/src/components/ModalRegisterItem.tsx index adf7ae2..453abb6 100644 --- a/src/components/ModalRegisterItem.tsx +++ b/src/components/ModalRegisterItem.tsx @@ -7,6 +7,7 @@ import { ModalBody, Flex, VStack, + useToast, } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; @@ -38,9 +39,12 @@ export function ModalRegisterItem({ const { handleSubmit, register, + reset, formState: { errors }, } = useForm(); + const toast = useToast(); + const createItem = useMutation( async ({ name, @@ -69,8 +73,24 @@ export function ModalRegisterItem({ const onSubmit = async (item: CreateItemFormData): Promise => { try { await createItem.mutateAsync(item); + + reset(); + + onClose(); + + toast({ + status: 'success', + title: 'Sucesso!', + description: 'O produto foi criado com sucesso!', + position: 'top-right', + }); } catch (err) { - console.log(err); + toast({ + status: 'error', + title: 'Algo deu errado!', + description: err.response.data?.error, + position: 'top-right', + }); } }; return ( diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index cccf08d..a8db417 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -1,6 +1,7 @@ import { Flex, Text, HStack, Img } from '@chakra-ui/react'; import { GetServerSideProps } from 'next'; import { Sidebar } from '../components/Sidebar'; +import { setupApiClient } from '../services/api'; import { withSSRAuth } from '../utils/withSSRAuth'; export default function Warehouse(): JSX.Element { @@ -32,6 +33,8 @@ export default function Warehouse(): JSX.Element { } export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { + setupApiClient(ctx); + console.log('inside stock: ', ctx); return { props: {}, }; diff --git a/src/services/api.ts b/src/services/api.ts index bb58ef8..7da091c 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -19,10 +19,14 @@ export function setupApiClient(ctx = undefined): AxiosInstance { return response; }, (error: AxiosError) => { - if (process.browser) { - signOut(); - } else { - return Promise.reject(new AuthTokenError()); + if (error.response.status === 401) { + if (error.response.data?.code === 'tokenExpiredOrInvalid') { + if (process.browser) { + signOut(); + } else { + return Promise.reject(new AuthTokenError()); + } + } } return Promise.reject(error); diff --git a/src/utils/withSSRAuth.ts b/src/utils/withSSRAuth.ts index 16a6b66..26e7828 100644 --- a/src/utils/withSSRAuth.ts +++ b/src/utils/withSSRAuth.ts @@ -1,3 +1,4 @@ +/* eslint-disable consistent-return */ import { GetServerSideProps, GetServerSidePropsContext, @@ -11,6 +12,7 @@ export function withSSRAuth

(fn: GetServerSideProps

): GetServerSideProps { ctx: GetServerSidePropsContext ): Promise> => { const cookies = parseCookies(ctx); + const token = cookies['@openwms.token']; if (!token) { return { @@ -24,6 +26,7 @@ export function withSSRAuth

(fn: GetServerSideProps

): GetServerSideProps { try { return await fn(ctx); } catch (err) { + console.log('Call Signout in withSSRAuth'); if (err instanceof AuthTokenError) { destroyCookie(ctx, '@openwms.token'); @@ -35,12 +38,5 @@ export function withSSRAuth

(fn: GetServerSideProps

): GetServerSideProps { }; } } - - return { - redirect: { - destination: '/login', - permanent: false, - }, - }; }; } From 9d3ecb7b90a0a4984402a0035f0d5257b88d6ddc Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sat, 19 Jun 2021 14:32:51 -0400 Subject: [PATCH 22/33] fix: auth behavior --- src/components/Button.tsx | 22 ++- src/components/Sidebar/SidebarNav.tsx | 2 +- src/pages/login.tsx | 24 ++- src/pages/settings.tsx | 274 +++++++++++++------------- src/pages/signup.tsx | 1 - src/pages/stock.tsx | 2 - src/styles/theme.ts | 6 + src/utils/withSSRAuth.ts | 1 - 8 files changed, 187 insertions(+), 145 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index a0ca591..8b88df6 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,14 +1,32 @@ import { Button as ChakraButton, ButtonProps as ChakraButtonProps, + CircularProgress, } from '@chakra-ui/react'; interface ButtonProps extends ChakraButtonProps { children: React.ReactNode; + isSubmitting?: boolean; } -export function Button({ children, ...rest }: ButtonProps): JSX.Element { - return ( +export function Button({ + isSubmitting = false, + children, + ...rest +}: ButtonProps): JSX.Element { + return isSubmitting ? ( + + + + ) : ( - Configurções + Configurações ); diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 14708cd..a522c5e 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,6 +1,7 @@ import { Grid, Image, Flex, Box, VStack, useToast } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; +import * as yup from 'yup'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; import { useAuth } from '../hooks/useAuth'; @@ -10,11 +11,19 @@ interface SignInFormData { password: string; } +const schema = yup.object().shape({ + login: yup + .string() + .required('O nome de usuário é obrigatório!') + .matches(/^\S+$/, 'O nome de usuário não pode conter espaços!'), + password: yup.string().required('A senha é obrigatória'), +}); + export default function Login(): JSX.Element { const { register, handleSubmit, - formState: { errors }, + formState: { errors, isSubmitting }, } = useForm(); const toast = useToast(); @@ -27,6 +36,8 @@ export default function Login(): JSX.Element { } ); + const onError = (error): void => error; + const onSubmit = async ({ login, password, @@ -58,7 +69,7 @@ export default function Login(): JSX.Element { alignItems="center" justifyContent="center" as="form" - onSubmit={handleSubmit(onSubmit)} + onSubmit={handleSubmit(onSubmit, onError)} > @@ -67,6 +78,7 @@ export default function Login(): JSX.Element { placeholder="Nome de usuário" label="Login" isDark + error={errors.login} {...register('login')} /> - diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 1126c49..8a6cf8e 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -6,6 +6,7 @@ import { Stack, useToast, Button as ChakraButton, + Grid, } from '@chakra-ui/react'; import { GetServerSideProps } from 'next'; import { useForm } from 'react-hook-form'; @@ -136,154 +137,157 @@ export default function Settings(): JSX.Element { p="8" flexDirection="column" w="100%" + h="100vh" > Configurações - - + + Perfil - - - - - - - - - - - - - - - + + + + + + + + + + + + + - + + Notificações - - - + + Ativar alertas por email: + setNotifyEmail(!notifyEmail)} > - Ativar alertas por email: - setNotifyEmail(!notifyEmail)} - > - {notifyEmail && ( - - )} - - - + )} + + + + Ativar alertas por sms: + setNotifySms(!notifySms)} > - Ativar alertas por sms: - setNotifySms(!notifySms)} - > - {notifySms && ( - - )} - - - - - - - + {notifySms && ( + + )} + + + + + + diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index 1a0b6e5..8aaf5ce 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -89,7 +89,6 @@ export default function SignUp(): JSX.Element { router.push('/login'); } catch (err) { - console.log(err); toast({ duration: 3000, status: 'error', diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index a8db417..375dd15 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -33,8 +33,6 @@ export default function Warehouse(): JSX.Element { } export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { - setupApiClient(ctx); - console.log('inside stock: ', ctx); return { props: {}, }; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index ad9136d..01801f2 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -16,11 +16,17 @@ export const theme = extendTheme({ white: '#F5F5F5', offWhite: '#EEEEEE', }, + white: '#F5F5F5', greenBtn: { 500: '#16DB93', 600: '#14C584', 700: '#12AF76', }, + yellowBtn: { + 500: '#FFB703', + 600: '#E6A503', + 700: '#CC9202', + }, }, fonts: { heading: 'Quicksand', diff --git a/src/utils/withSSRAuth.ts b/src/utils/withSSRAuth.ts index 26e7828..57061cb 100644 --- a/src/utils/withSSRAuth.ts +++ b/src/utils/withSSRAuth.ts @@ -26,7 +26,6 @@ export function withSSRAuth

(fn: GetServerSideProps

): GetServerSideProps { try { return await fn(ctx); } catch (err) { - console.log('Call Signout in withSSRAuth'); if (err instanceof AuthTokenError) { destroyCookie(ctx, '@openwms.token'); From 37ebfbcf3c7c808e9ccfdfc264cb7d15b5c3b81b Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sat, 19 Jun 2021 15:32:51 -0400 Subject: [PATCH 23/33] create page of email verification result --- public/images/denyMail.svg | 18 +++++++ public/images/successMail.svg | 17 ++++++ src/components/LayoutMailVerification.tsx | 36 +++++++++++++ src/pages/verify/[verify].tsx | 64 +++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 public/images/denyMail.svg create mode 100644 public/images/successMail.svg create mode 100644 src/components/LayoutMailVerification.tsx create mode 100644 src/pages/verify/[verify].tsx diff --git a/public/images/denyMail.svg b/public/images/denyMail.svg new file mode 100644 index 0000000..0a918f0 --- /dev/null +++ b/public/images/denyMail.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/successMail.svg b/public/images/successMail.svg new file mode 100644 index 0000000..13143a2 --- /dev/null +++ b/public/images/successMail.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/components/LayoutMailVerification.tsx b/src/components/LayoutMailVerification.tsx new file mode 100644 index 0000000..9925179 --- /dev/null +++ b/src/components/LayoutMailVerification.tsx @@ -0,0 +1,36 @@ +import { Flex, VStack, Image, Text, CircularProgress } from '@chakra-ui/react'; + +interface LayoutMailVerificationProps { + children: React.ReactElement | React.ReactElement[]; +} + +export function LayoutMailVerification({ + children, +}: LayoutMailVerificationProps): JSX.Element { + return ( + + + + {children} + + + ); +} diff --git a/src/pages/verify/[verify].tsx b/src/pages/verify/[verify].tsx new file mode 100644 index 0000000..76f433e --- /dev/null +++ b/src/pages/verify/[verify].tsx @@ -0,0 +1,64 @@ +import { Flex, VStack, Image, Text, Spinner } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import { LayoutMailVerification } from '../../components/LayoutMailVerification'; + +export default function MailVerification(): JSX.Element { + const [status, setStatus] = useState(''); + const { query } = useRouter(); + const { verify } = query; + + useEffect(() => { + setStatus(''); + }, []); + + if (!status) { + return ( + + + Estamos verificado seu e-mail... + + + + ); + } + + if (status === 'success') { + return ( + + + + Email verificado com sucesso! + + + ); + } + + if (status === 'error') { + return ( + + + + Token não existe + + + ); + } + + if (status === 'expired') { + return ( + + + + + O link de confirmação do e-mail está expirado! + + + Enviamos outro e-mail com um novo link para que possa confirmar seu + e-mail! + + + + ); + } +} From bb550251f873761fb16a3ce9ba9f37cc383f5b2e Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sat, 19 Jun 2021 17:17:09 -0400 Subject: [PATCH 24/33] feature: implements email verification functionality --- src/components/LayoutMailVerification.tsx | 2 +- src/pages/verify/[verify].tsx | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/LayoutMailVerification.tsx b/src/components/LayoutMailVerification.tsx index 9925179..6de3711 100644 --- a/src/components/LayoutMailVerification.tsx +++ b/src/components/LayoutMailVerification.tsx @@ -1,4 +1,4 @@ -import { Flex, VStack, Image, Text, CircularProgress } from '@chakra-ui/react'; +import { Flex, VStack, Image } from '@chakra-ui/react'; interface LayoutMailVerificationProps { children: React.ReactElement | React.ReactElement[]; diff --git a/src/pages/verify/[verify].tsx b/src/pages/verify/[verify].tsx index 76f433e..e8c4824 100644 --- a/src/pages/verify/[verify].tsx +++ b/src/pages/verify/[verify].tsx @@ -1,16 +1,23 @@ -import { Flex, VStack, Image, Text, Spinner } from '@chakra-ui/react'; +import { VStack, Image, Text, Spinner } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { LayoutMailVerification } from '../../components/LayoutMailVerification'; +import { api } from '../../services/apiClient'; export default function MailVerification(): JSX.Element { const [status, setStatus] = useState(''); + const { query } = useRouter(); - const { verify } = query; + + const { verify: token } = query; useEffect(() => { - setStatus(''); - }, []); + if (token) { + api + .patch(`/users/verify/${token}`) + .then(response => setStatus(response.data.status)); + } + }, [token]); if (!status) { return ( From fa1e68dec3aa03d534ff1da798aae51cc78d81e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeandroGon=C3=A7alves?= Date: Sat, 19 Jun 2021 18:23:42 -0300 Subject: [PATCH 25/33] Create page settings --- src/contexts/AuthContext.tsx | 45 +++- src/pages/settings.tsx | 394 +++++++++++++++++++++-------------- 2 files changed, 287 insertions(+), 152 deletions(-) diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 295c5a8..b304323 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -19,9 +19,19 @@ type SignInCredentials = { password: string; }; +type UserInformation = { + name?: string; + email?: string; + login?: string; + phone?: string; + newPassword?: string; + currentPassword?: string; +}; + export type AuthContextData = { signIn(credentials: SignInCredentials): Promise; signOut: () => void; + updateUserInformation: (userCredentials: UserInformation) => Promise; user: User; isAuthenticated: boolean; }; @@ -105,8 +115,41 @@ function AuthProvider({ children }: AuthProviderProps): JSX.Element { } } + async function updateUserInformation({ + email = '', + login = '', + name = '', + phone = '', + newPassword = '', + currentPassword = '', + }: UserInformation): Promise { + try { + const response = await api.put(`users/${user.id}`, { + name, + email: user?.email === email ? '' : email, + login: user?.login === login ? '' : login, + phone: user?.phone === phone ? '' : phone, + newPassword, + currentPassword, + }); + const { user: userData } = response.data; + localStorage.setItem('@openwms.user', JSON.stringify(userData)); + setUser(userData); + return true; + } catch (err) { + toast({ + status: 'error', + title: err.response.data.error, + position: 'top-right', + }); + return false; + } + } + return ( - + {children} ); diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 1126c49..84572f1 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -5,24 +5,28 @@ import { Text, Stack, useToast, + SimpleGrid, Button as ChakraButton, } from '@chakra-ui/react'; +import { RiPencilLine, RiSaveLine } from 'react-icons/ri'; import { GetServerSideProps } from 'next'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; import { useState } from 'react'; +import { useEffect } from 'react'; import { Input } from '../components/Input'; import { Sidebar } from '../components/Sidebar'; import { Button } from '../components/Button'; import { withSSRAuth } from '../utils/withSSRAuth'; import { useAuth } from '../hooks/useAuth'; -import { api } from '../services/apiClient'; interface RegisterFormData { name: string; email: string; + login: string; + phone: string; } interface PasswordFormData { oldPassword: string; @@ -31,8 +35,13 @@ interface PasswordFormData { } const registerSchema = yup.object().shape({ - name: yup.string(), - email: yup.string(), + name: yup.string().required('O campo de nome nao pode estar vazio!'), + email: yup + .string() + .email('O email não é valido!') + .required('O campo de email nao pode estar vazio!'), + login: yup.string().required('O campo de login nao pode estar vazio!'), + phone: yup.string().required('O campo de telefone nao pode estar vazio!'), }); const passwordSchema = yup.object().shape({ @@ -46,42 +55,54 @@ const passwordSchema = yup.object().shape({ export default function Settings(): JSX.Element { const toast = useToast(); - const { user } = useAuth(); + const { user, updateUserInformation } = useAuth(); + + const [notifyEmail, setNotifyEmail] = useState(true); + const [notifySms, setNotifySms] = useState(true); - const [notifyEmail, setNotifyEmail] = useState(false); - const [notifySms, setNotifySms] = useState(false); + const [isEditing, setIsEditing] = useState(false); const { handleSubmit, register, + setValue, formState: { errors }, } = useForm({ resolver: yupResolver(registerSchema), - defaultValues: { name: user?.name || '', email: user?.email || '' }, }); const { handleSubmit: handleSubmitPassword, register: registerPassword, + reset, formState: { errors: errorsPassword }, } = useForm({ resolver: yupResolver(passwordSchema) }); + useEffect(() => { + if (user) { + setValue('name', user.name); + setValue('email', user.email); + setValue('login', user.login); + setValue('phone', user.phone); + } + }, [user]); + const handleChangeRegister = async ({ name, email, + login, + phone, }: RegisterFormData): Promise => { try { - // await api.put(`users/${user.id}`, { - // name, - // email, - // }); + await updateUserInformation({ name, email, login, phone }); toast({ duration: 3000, status: 'success', title: 'Sucesso!', - description: 'nome e/ou email alterados!', + description: 'Seus dados foram alterados!', position: 'top-right', }); + setIsEditing(false); } catch (err) { toast({ duration: 3000, @@ -96,16 +117,29 @@ export default function Settings(): JSX.Element { const handleChangePassword = async ({ oldPassword, newPassword, - newPasswordConfirm, }: PasswordFormData): Promise => { try { - console.log({ oldPassword, newPassword, newPasswordConfirm }); + const isSuccess = await updateUserInformation({ + currentPassword: oldPassword, + newPassword, + }); + if (!isSuccess) { + return; + } + toast({ + duration: 3000, + status: 'success', + title: 'Sucesso!', + description: 'Sua senha foi alterada!', + position: 'top-right', + }); + reset(); } catch (err) { toast({ duration: 3000, status: 'error', title: 'Algo deu errado', - description: 'Usuário/senha incorretos!', + description: 'Não conseguimos atualizar sua senha!', position: 'top-right', }); } @@ -114,7 +148,6 @@ export default function Settings(): JSX.Element { const handleAlerts = async (event): Promise => { try { event.preventDefault(); - console.log({ notifyEmail, notifySms }); } catch (err) { toast({ duration: 3000, @@ -132,158 +165,217 @@ export default function Settings(): JSX.Element { - - Configurações - - - + + + Configurações + + + + Perfil - - - - - - - - - - - - - - - + + - + Notificações - - - + + Ativar alertas por email: + setNotifyEmail(!notifyEmail)} > - Ativar alertas por email: - setNotifyEmail(!notifyEmail)} - > - {notifyEmail && ( - - )} - - - + )} + + + + Ativar alertas por sms: + setNotifySms(!notifySms)} > - Ativar alertas por sms: - setNotifySms(!notifySms)} - > - {notifySms && ( - - )} - - - - - - - + {notifySms && ( + + )} + + + + + + + + + + + + + + From 3ff09a45459226bf94ac9705e47364355ebc60c8 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sat, 19 Jun 2021 19:02:50 -0400 Subject: [PATCH 26/33] refactoring: refactored styles of Input and scrollbar --- src/components/ModalRegisterItem.tsx | 12 ++++++- src/components/Sidebar/SidebarLink.tsx | 4 ++- src/contexts/AuthContext.tsx | 5 +-- src/pages/login.tsx | 5 ++- src/pages/products.tsx | 17 ++++++++- src/pages/settings.tsx | 50 ++++++++++++-------------- src/styles/theme.ts | 5 +++ 7 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/components/ModalRegisterItem.tsx b/src/components/ModalRegisterItem.tsx index 453abb6..edc75a2 100644 --- a/src/components/ModalRegisterItem.tsx +++ b/src/components/ModalRegisterItem.tsx @@ -115,30 +115,40 @@ export function ModalRegisterItem({ onSubmit={handleSubmit(onSubmit)} > - + diff --git a/src/components/Sidebar/SidebarLink.tsx b/src/components/Sidebar/SidebarLink.tsx index 8ea4ca5..7772f70 100644 --- a/src/components/Sidebar/SidebarLink.tsx +++ b/src/components/Sidebar/SidebarLink.tsx @@ -31,9 +31,11 @@ export function SidebarLink({ pr="16" borderLeftRadius="25" textDecorationLine="none" + maxW={240} + w="100%" > - + {children} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index b304323..081f086 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -46,7 +46,7 @@ let authChannel: BroadcastChannel; function signOut(): void { destroyCookie(undefined, '@openwms.token'); - localStorage.removeItem('openwms.user'); + localStorage.removeItem('@openwms.user'); authChannel.postMessage('signOut'); @@ -109,7 +109,8 @@ function AuthProvider({ children }: AuthProviderProps): JSX.Element { } catch (err) { toast({ status: 'error', - title: err.response.data.error, + title: 'Algo deu errado!', + description: err.response.data.error, position: 'top-right', }); } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index a522c5e..61195a8 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,4 +1,5 @@ import { Grid, Image, Flex, Box, VStack, useToast } from '@chakra-ui/react'; +import { yupResolver } from '@hookform/resolvers/yup'; import { useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; import * as yup from 'yup'; @@ -24,7 +25,9 @@ export default function Login(): JSX.Element { register, handleSubmit, formState: { errors, isSubmitting }, - } = useForm(); + } = useForm({ + resolver: yupResolver(schema), + }); const toast = useToast(); diff --git a/src/pages/products.tsx b/src/pages/products.tsx index 9ac65c3..bd09b0f 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -39,7 +39,22 @@ export default function Products(): JSX.Element { - + diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index b550572..63ac865 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -7,7 +7,6 @@ import { useToast, SimpleGrid, Button as ChakraButton, - Grid, } from '@chakra-ui/react'; import { RiPencilLine, RiSaveLine } from 'react-icons/ri'; import { GetServerSideProps } from 'next'; @@ -86,7 +85,7 @@ export default function Settings(): JSX.Element { setValue('login', user.login); setValue('phone', user.phone); } - }, [user]); + }, [user, setValue]); const handleChangeRegister = async ({ name, @@ -109,7 +108,7 @@ export default function Settings(): JSX.Element { duration: 3000, status: 'error', title: 'Algo deu errado', - description: 'Usuário/senha incorretos!', + description: err.response.data.error, position: 'top-right', }); } @@ -146,7 +145,7 @@ export default function Settings(): JSX.Element { } }; - const handleAlerts = async (event): Promise => { + const handleAlerts = async (event: React.FormEvent): Promise => { try { event.preventDefault(); } catch (err) { @@ -179,32 +178,44 @@ export default function Settings(): JSX.Element { Configurações - + Perfil Date: Sun, 20 Jun 2021 11:15:37 -0300 Subject: [PATCH 27/33] Stock screen and modal created --- src/components/ModalRegisterStock.tsx | 150 +++++++++++++++++++++++++ src/components/Sidebar/SidebarLink.tsx | 2 +- src/pages/products.tsx | 1 + src/pages/stock.tsx | 109 ++++++++++++++---- 4 files changed, 240 insertions(+), 22 deletions(-) create mode 100644 src/components/ModalRegisterStock.tsx diff --git a/src/components/ModalRegisterStock.tsx b/src/components/ModalRegisterStock.tsx new file mode 100644 index 0000000..58a7bd7 --- /dev/null +++ b/src/components/ModalRegisterStock.tsx @@ -0,0 +1,150 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Flex, + VStack, + useToast, +} from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { useMutation } from 'react-query'; +import { api } from '../services/apiClient'; +import { queryClient } from '../services/queryClient'; + +import { Button } from './Button'; +import { Input } from './Input'; + +interface ModalRegisterStockProps { + isOpen: boolean; + onClose: () => void; +} + +interface CreateStockFormData { + name: string; + category: string; + quantity: number; + expirationDate?: number; + value: number; +} + +export function ModalRegisterStock({ + isOpen, + onClose, +}: ModalRegisterStockProps): JSX.Element { + const { + handleSubmit, + register, + reset, + formState: { errors }, + } = useForm(); + + const toast = useToast(); + + const createStock = useMutation( + async ({ + name, + category, + quantity, + expirationDate, + value, + }: CreateStockFormData) => { + const response = await api.post('stock', { + name, + category, + quantity, + expirationDate, + value, + }); + + return response.data.user; + }, + { + onSuccess: () => { + queryClient.invalidateQueries('items'); + }, + } + ); + + const onSubmit = async (stock: CreateStockFormData): Promise => { + try { + await createStock.mutateAsync(stock); + + reset(); + + onClose(); + + toast({ + status: 'success', + title: 'Sucesso!', + description: 'O estoque foi criado com sucesso!', + position: 'top-right', + }); + } catch (err) { + toast({ + status: 'error', + title: 'Algo deu errado!', + description: err.response.data?.error, + position: 'top-right', + }); + } + }; + return ( + + + + + Cadastro de estoque + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/Sidebar/SidebarLink.tsx b/src/components/Sidebar/SidebarLink.tsx index 8ea4ca5..76bb654 100644 --- a/src/components/Sidebar/SidebarLink.tsx +++ b/src/components/Sidebar/SidebarLink.tsx @@ -28,7 +28,7 @@ export function SidebarLink({ pl="4" pt="2.5" pb="2" - pr="16" + pr="22" borderLeftRadius="25" textDecorationLine="none" > diff --git a/src/pages/products.tsx b/src/pages/products.tsx index 9ac65c3..29db97c 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -67,6 +67,7 @@ export default function Products(): JSX.Element { + diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index 375dd15..73e3f6d 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -1,33 +1,100 @@ -import { Flex, Text, HStack, Img } from '@chakra-ui/react'; +import { + Flex, + Text, + HStack, + Img, + Box, + Heading, + Table, + Tbody, + Td, + Th, + Thead, + Tr, + useDisclosure, +} from '@chakra-ui/react'; import { GetServerSideProps } from 'next'; +import { ModalRegisterStock } from '../components/ModalRegisterStock'; import { Sidebar } from '../components/Sidebar'; +import { Button } from '../components/Button'; import { setupApiClient } from '../services/api'; import { withSSRAuth } from '../utils/withSSRAuth'; export default function Warehouse(): JSX.Element { + const { isOpen, onClose, onOpen } = useDisclosure(); return ( - + - - - - - Estoque - - + + + + + Cadastro de produtos + + + + + Lista de produtos + + + + + + +
12/12/2020
1 1 Produto 1 Bebidas
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CódigoNomeCategoriaEntradaSaídaQtdeUndValorCriado em
1Produto 1Bebidas16/05/202118/06/202110CaixaR$ 2500,0016/05/2021
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
+
+ + ); } From 9992052bee80cb43866535bb5277eeba4883389e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeandroGon=C3=A7alves?= Date: Sun, 20 Jun 2021 18:42:09 -0300 Subject: [PATCH 28/33] Add README.md --- README.md | 44 +++++++++++++++++++++++++++++++++++ public/images/homeScreen.png | Bin 0 -> 160249 bytes 2 files changed, 44 insertions(+) create mode 100644 README.md create mode 100644 public/images/homeScreen.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e0d5ee --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +![OpenWMS](/public/images/logoHorizontal.svg) + +## Apresentação +Toda Empresa, dês das pequenas, até as grandes precisam de um controle de seu estoque, muitas empresas recorrem a métodos mais demorados como planilhas pois existe ou alto custo em sistemas especializados. Nosso objetivo foi criar um WMS(Warehouse management system) gratuito e acessível para todos, onde qualquer empresa consiga criar sua conta e realizar um controle muito mais preciso de seus inventario, com alertas de vencimento e de estoque mínimo. + + + * [Apresentação](#Apresentação) + * [Tecnologias](#Tecnologias) + * [Instalação](#Instalação) + + +## Design +![OpenWMS](/public/images/homeScreen.png) +O design foi construído com o foco em simplicidade, tornando a aplicação muito intuitiva e amigável para qualquer usuário. + +## Tecnologias +O projeto foi desenvolvido utilizando as tecnologias mais atuais como: ++ [TS(typescript)](https://www.typescriptlang.org) ++ [NextJs](https://nextjs.org) ++ [Chakra-ui](https://chakra-ui.com) ++ [react-query](react-query) ++ [react-hook-form](https://react-hook-form.com) ++ [yup](https://github.com/jquense/yup) + +## Instalação +Para executar nosso projeto voce vai precisar do **npm/yarn** e do **git**. + +Antes de tudo você precisara clonar nosso repositório, você pode utilizar o comando: +``` +git clone https://github.com/regisfaria/open_wms_backend +``` + +Depois do repositório clonado você deve executar o seguinte comando: + +```shell +yarn OU npm install +``` + +Em seguida execute o comando: + +```shell +yarn start OU npm start +``` +Agora seu projeto vai estar disponível em: **http://localhost:3000**. \ No newline at end of file diff --git a/public/images/homeScreen.png b/public/images/homeScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3dfc650039106aa4fa9c2d8322f9f6886e5473 GIT binary patch literal 160249 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 Date: Sun, 20 Jun 2021 20:11:32 -0400 Subject: [PATCH 29/33] refactoring: last adjusts --- package.json | 4 + src/components/DropdownSearch.tsx | 164 ++++++++++ src/components/ModalDeleteUser.tsx | 67 +++++ src/components/ModalRegisterItem.tsx | 17 +- src/components/ModalRegisterStock.tsx | 98 ++++-- src/components/Sidebar/SidebarLink.tsx | 2 +- src/components/Sidebar/SidebarProfile.tsx | 10 +- src/pages/dashboard.tsx | 137 ++++++++- src/pages/products.tsx | 348 ++++++++-------------- src/pages/settings.tsx | 7 + src/pages/signup.tsx | 3 +- src/pages/stock.tsx | 247 ++++++++++----- src/services/api.ts | 4 +- src/utils/searchFilterItems.ts | 21 ++ yarn.lock | 199 ++++++++++++- 15 files changed, 974 insertions(+), 354 deletions(-) create mode 100644 src/components/DropdownSearch.tsx create mode 100644 src/components/ModalDeleteUser.tsx create mode 100644 src/utils/searchFilterItems.ts diff --git a/package.json b/package.json index 8888364..c1a8a87 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,16 @@ "@emotion/react": "^11", "@emotion/styled": "^11", "@hookform/resolvers": "^2.5.2", + "apexcharts": "^3.27.1", "axios": "^0.21.1", + "date-fns": "^2.22.1", "framer-motion": "^4", "next": "11.0.0", "nookies": "^2.5.2", "react": "17.0.2", + "react-apexcharts": "^1.3.9", "react-dom": "17.0.2", + "react-dropdown-select": "^4.7.4", "react-hook-form": "^7.8.8", "react-icons": "^4.2.0", "react-query": "^3.17.2", diff --git a/src/components/DropdownSearch.tsx b/src/components/DropdownSearch.tsx new file mode 100644 index 0000000..728367c --- /dev/null +++ b/src/components/DropdownSearch.tsx @@ -0,0 +1,164 @@ +import { Flex, List, ListItem, Input as ChakraInput } from '@chakra-ui/react'; +import { useState, useRef, useEffect } from 'react'; + +interface Item { + userId: string; + name: string; + id: string; +} + +interface DropdownSearchProps { + name: string; + listItems: Item[]; + setValue: (name: string, value: string) => void; +} + +const DropdownSearchComponent = ({ + name, + listItems, + setValue, +}: DropdownSearchProps): JSX.Element => { + const [visible, setVisible] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const [selectedItemName, setSelectedItemName] = useState(''); + const [items, setItems] = useState([]); + const dropdownRef = useRef(null); + + useEffect(() => { + setItems(listItems); + }, [listItems]); + + const handleChange = (e: React.FormEvent): void => { + setSearchValue(e.currentTarget.value); + if (!visible) { + setVisible(true); + } + }; + + const selectItem = (item: Item): void => { + setSelectedItemName(item.name); + setValue(name, item.id); + setVisible(false); + }; + + useEffect((): void => { + const lowerCaseQuery = searchValue.toLowerCase(); + const filteredList = searchValue + ? listItems.filter(x => x.name.toLowerCase().includes(lowerCaseQuery)) + : listItems; + setItems(filteredList); + }, [listItems, setItems, searchValue]); + + return ( + + { + setVisible(true); + }} + size="lg" + isReadOnly + aria-label={name} + fontSize="xl" + color="main.darkBlue" + borderWidth="3px" + borderColor="transparent" + borderRadius="" + bgColor="main.offWhite" + css={{ + borderBottomRightRadius: visible ? '0' : '20px', + borderBottomLeftRadius: visible ? '0' : '20px', + borderTopRightRadius: 20, + borderTopLeftRadius: 20, + }} + py={8} + pr={8} + /> + + {visible && ( + + + + {!items && Produto não encontrado} + + {items && + items.map(item => ( + selectItem(item)} + w="100%" + textAlign="initial" + pl={6} + py={2} + > + {item.name} + + ))} + + + )} + + ); +}; + +export const DropdownSearch = DropdownSearchComponent; diff --git a/src/components/ModalDeleteUser.tsx b/src/components/ModalDeleteUser.tsx new file mode 100644 index 0000000..97aaec2 --- /dev/null +++ b/src/components/ModalDeleteUser.tsx @@ -0,0 +1,67 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Text, + Button, + useToast, +} from '@chakra-ui/react'; +import { useAuth } from '../hooks/useAuth'; +import { api } from '../services/apiClient'; + +interface ModalDeleteUserProps { + isOpen: boolean; + onClose: () => void; +} + +export function ModalDeleteUser({ + isOpen, + onClose, +}: ModalDeleteUserProps): JSX.Element { + const { signOut } = useAuth(); + const toast = useToast(); + + async function handleDeleteUser(): Promise { + try { + await api.delete('/users'); + signOut(); + } catch (err) { + toast({ + status: 'error', + title: 'Algo deu errado!', + description: err.response?.data.error, + }); + } + } + return ( + + + + + Deletar Conta + + + + + Você tem certeza que deseja deletar sua conta? + + + Está ação é irreversível, você perderar todos os seus dados criados! + + + + + + ); +} diff --git a/src/components/ModalRegisterItem.tsx b/src/components/ModalRegisterItem.tsx index edc75a2..2d56ecf 100644 --- a/src/components/ModalRegisterItem.tsx +++ b/src/components/ModalRegisterItem.tsx @@ -23,7 +23,6 @@ interface ModalRegisterItemProps { } interface CreateItemFormData { - code?: string; name: string; category: string; minimumStock?: number; @@ -72,6 +71,12 @@ export function ModalRegisterItem({ const onSubmit = async (item: CreateItemFormData): Promise => { try { + const removeEmptyItem = item; + Object.keys(removeEmptyItem).forEach(key => { + if (removeEmptyItem[key] === '' || removeEmptyItem[key] == null) { + delete removeEmptyItem[key]; + } + }); await createItem.mutateAsync(item); reset(); @@ -115,12 +120,6 @@ export function ModalRegisterItem({ onSubmit={handleSubmit(onSubmit)} > - @@ -149,6 +150,8 @@ export function ModalRegisterItem({ name="minimumStock" label="Estoque mínimo (opcional)" error={errors.minimumStock} + defaultValue={0} + type="number" {...register('minimumStock')} /> diff --git a/src/components/ModalRegisterStock.tsx b/src/components/ModalRegisterStock.tsx index 58a7bd7..5afe010 100644 --- a/src/components/ModalRegisterStock.tsx +++ b/src/components/ModalRegisterStock.tsx @@ -8,9 +8,14 @@ import { Flex, VStack, useToast, + Select, + Box, + Text, } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; -import { useMutation } from 'react-query'; +import { useMutation, useQuery } from 'react-query'; +import { DropdownSearch } from './DropdownSearch'; import { api } from '../services/apiClient'; import { queryClient } from '../services/queryClient'; @@ -23,21 +28,37 @@ interface ModalRegisterStockProps { } interface CreateStockFormData { - name: string; - category: string; + itemId: string; quantity: number; expirationDate?: number; value: number; + movement: string; +} + +interface Item { + userId: string; + name: string; + category: string; + minimumStock: number; + daysToNotifyExpirationDate: number; + measureunity: string; + id: string; +} + +interface ItemsData { + items: Item[]; } export function ModalRegisterStock({ isOpen, onClose, }: ModalRegisterStockProps): JSX.Element { + const [items, setItems] = useState({ items: [] }); const { handleSubmit, register, reset, + setValue, formState: { errors }, } = useForm(); @@ -45,15 +66,14 @@ export function ModalRegisterStock({ const createStock = useMutation( async ({ - name, - category, + itemId, quantity, expirationDate, value, + movement, }: CreateStockFormData) => { - const response = await api.post('stock', { - name, - category, + const response = await api.post(`stocks/${movement}`, { + itemId, quantity, expirationDate, value, @@ -63,19 +83,30 @@ export function ModalRegisterStock({ }, { onSuccess: () => { - queryClient.invalidateQueries('items'); + queryClient.invalidateQueries('stock'); }, } ); - const onSubmit = async (stock: CreateStockFormData): Promise => { - try { - await createStock.mutateAsync(stock); + const { isLoading, error, data } = useQuery('filterItems', () => + api.get('/items').then(response => response.data) + ); - reset(); + useEffect(() => { + setItems(data); + }, [data]); + const onSubmit = async (item: CreateStockFormData): Promise => { + const removeEmptyItem = item; + Object.keys(removeEmptyItem).forEach(key => { + if (removeEmptyItem[key] === '' || removeEmptyItem[key] == null) { + delete removeEmptyItem[key]; + } + }); + try { + await createStock.mutateAsync(removeEmptyItem); + reset(); onClose(); - toast({ status: 'success', title: 'Sucesso!', @@ -83,6 +114,7 @@ export function ModalRegisterStock({ position: 'top-right', }); } catch (err) { + console.log(err.response); toast({ status: 'error', title: 'Algo deu errado!', @@ -113,31 +145,43 @@ export function ModalRegisterStock({ onSubmit={handleSubmit(onSubmit)} > - - + + + Movimentação + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CódigoNomeCategoriaTipoDataQtdeUndValorCriado em
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
11Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
-
+ + + + {isLoading ? ( + + ) : ( + + + + + + + + + + + + + + {listItems && + listItems?.map(item => ( + + + + + + + + + ))} + +
NomeCategoriaEstoque Min.Dias para aviso de vencimentoUndCriado em
{item.name}{item.category}{item.minimumStock} + {item.daysToNotifyExpirationDate} + {item.measureunity}{item.createdat}
+
+ )}
- + ); diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 63ac865..995e310 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -6,6 +6,7 @@ import { Stack, useToast, SimpleGrid, + useDisclosure, Button as ChakraButton, } from '@chakra-ui/react'; import { RiPencilLine, RiSaveLine } from 'react-icons/ri'; @@ -21,6 +22,7 @@ import { Sidebar } from '../components/Sidebar'; import { Button } from '../components/Button'; import { withSSRAuth } from '../utils/withSSRAuth'; import { useAuth } from '../hooks/useAuth'; +import { ModalDeleteUser } from '../components/ModalDeleteUser'; interface RegisterFormData { name: string; @@ -55,6 +57,7 @@ const passwordSchema = yup.object().shape({ export default function Settings(): JSX.Element { const toast = useToast(); + const { isOpen, onClose, onOpen } = useDisclosure(); const { user, updateUserInformation } = useAuth(); const [notifyEmail, setNotifyEmail] = useState(true); @@ -375,7 +378,11 @@ export default function Settings(): JSX.Element {
+ + Deletar Conta + + ); } diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx index 8aaf5ce..581fab7 100644 --- a/src/pages/signup.tsx +++ b/src/pages/signup.tsx @@ -89,11 +89,12 @@ export default function SignUp(): JSX.Element { router.push('/login'); } catch (err) { + console.log(err.response); toast({ duration: 3000, status: 'error', title: 'Alguma coisa deu errado!', - description: err.response.data.error, + description: err.response?.data.error, position: 'top-right', }); } diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index 73e3f6d..2c415e9 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -2,7 +2,7 @@ import { Flex, Text, HStack, - Img, + Image, Box, Heading, Table, @@ -10,90 +10,197 @@ import { Td, Th, Thead, + Icon, Tr, useDisclosure, + Spinner, + Select, + Grid, } from '@chakra-ui/react'; +import { useQuery } from 'react-query'; +import { useForm } from 'react-hook-form'; import { GetServerSideProps } from 'next'; +import { useEffect, useState } from 'react'; +import { RiArrowUpFill, RiArrowDownFill } from 'react-icons/ri'; import { ModalRegisterStock } from '../components/ModalRegisterStock'; import { Sidebar } from '../components/Sidebar'; import { Button } from '../components/Button'; -import { setupApiClient } from '../services/api'; import { withSSRAuth } from '../utils/withSSRAuth'; +import { api } from '../services/apiClient'; -export default function Warehouse(): JSX.Element { +interface Stock { + id: string; + itemId: string; + type: string; + quantity: number; + value: number; + expirationDate: string; + createdAt: string; +} + +interface Item { + id: string; + name: string; +} + +interface StockData { + stocks: Stock[]; + item: Item; +} + +interface FilterFormData { + filterByItem: string; +} + +export default function Stock(): JSX.Element { const { isOpen, onClose, onOpen } = useDisclosure(); + const { handleSubmit, register } = useForm(); + const [filterItems, setFilterItems] = useState(''); + + // const [stockListByItems, setStockListByItems] = useState({ + // stocks: [], + // item: null, + // }); + + const { data: listItems } = useQuery('getItems', () => + api.get('/items').then(response => response.data.items) + ); + + const { isLoading, refetch, data } = useQuery( + ['getStock', filterItems], + () => + api.get(`/stocks/byItem/${filterItems}`).then(response => response.data), + { + refetchOnWindowFocus: false, + enabled: false, + } + ); + + const stockListByItems = data as StockData; + + useEffect(() => { + if (filterItems) { + console.log(filterItems); + refetch(); + } + }, [filterItems, refetch]); + + const onSubmit = ({ filterByItem }: FilterFormData): void => { + setFilterItems(filterByItem); + refetch(); + }; return ( - - - - - Cadastro de produtos + + + + + Estoque + + + + + Movimentação de estoque + + + - - - - Lista de produtos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CódigoNomeCategoriaEntradaSaídaQtdeUndValorCriado em
1Produto 1Bebidas16/05/202118/06/202110CaixaR$ 2500,0016/05/2021
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
1Produto 1BebidasEntrada20/01/202110CaixaR$ 2500,0012/12/2020
-
+ +
+ + {isLoading && } + {stockListByItems?.item && ( + + + + + + + + + + + + + + {stockListByItems.stocks.map(stock => ( + + + + + + ))} + +
Item: {stockListByItems.item.name}
MovimentaçãoQtdeValor
+ {stock.type === 'input' ? ( + + ) : ( + + )} + R$ {stock.quantity}{stock.value}
+
+ )}
-
+
); diff --git a/src/services/api.ts b/src/services/api.ts index 7da091c..423781e 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -19,8 +19,8 @@ export function setupApiClient(ctx = undefined): AxiosInstance { return response; }, (error: AxiosError) => { - if (error.response.status === 401) { - if (error.response.data?.code === 'tokenExpiredOrInvalid') { + if (error.response?.status === 401) { + if (error.response.data?.statusCode === 'tokenExpiredOrInvalid') { if (process.browser) { signOut(); } else { diff --git a/src/utils/searchFilterItems.ts b/src/utils/searchFilterItems.ts new file mode 100644 index 0000000..5a5b182 --- /dev/null +++ b/src/utils/searchFilterItems.ts @@ -0,0 +1,21 @@ +interface Item { + userId: string; + name: string; + category: string; + minimumStock: number; + daysToNotifyExpirationDate: number; + measureUnity: string; + id: string; +} + +export const searchFilterItems = ( + searchValue: string, + list: Item[], + searchBy = 'name' +): Item[] => { + const lowerCaseQuery = searchValue.toLowerCase(); + const filteredList = searchValue + ? list.filter(x => x[searchBy].toLowerCase().includes(lowerCaseQuery)) + : list; + return filteredList; +}; diff --git a/yarn.lock b/yarn.lock index 6a01c21..182b4ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -91,7 +91,7 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== @@ -893,6 +893,16 @@ source-map "^0.5.7" stylis "^4.0.3" +"@emotion/cache@^10.0.27": + version "10.0.29" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" + integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ== + dependencies: + "@emotion/sheet" "0.9.4" + "@emotion/stylis" "0.8.5" + "@emotion/utils" "0.11.3" + "@emotion/weak-memoize" "0.2.5" + "@emotion/cache@^11.4.0": version "11.4.0" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0" @@ -904,12 +914,33 @@ "@emotion/weak-memoize" "^0.2.5" stylis "^4.0.3" -"@emotion/hash@^0.8.0": +"@emotion/core@^10.0.27": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3" + integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/cache" "^10.0.27" + "@emotion/css" "^10.0.27" + "@emotion/serialize" "^0.11.15" + "@emotion/sheet" "0.9.4" + "@emotion/utils" "0.11.3" + +"@emotion/css@^10.0.27": + version "10.0.27" + resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c" + integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw== + dependencies: + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + babel-plugin-emotion "^10.0.27" + +"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/is-prop-valid@^0.8.2": +"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.2": version "0.8.8" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== @@ -946,6 +977,17 @@ "@emotion/weak-memoize" "^0.2.5" hoist-non-react-statics "^3.3.1" +"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": + version "0.11.16" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" + integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg== + dependencies: + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/unitless" "0.7.5" + "@emotion/utils" "0.11.3" + csstype "^2.5.7" + "@emotion/serialize@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965" @@ -957,11 +999,34 @@ "@emotion/utils" "^1.0.0" csstype "^3.0.2" +"@emotion/sheet@0.9.4": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" + integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== + "@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.1.tgz#245f54abb02dfd82326e28689f34c27aa9b2a698" integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g== +"@emotion/styled-base@^10.0.27": + version "10.0.31" + resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a" + integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/is-prop-valid" "0.8.8" + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + +"@emotion/styled@^10.0.27": + version "10.0.27" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" + integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== + dependencies: + "@emotion/styled-base" "^10.0.27" + babel-plugin-emotion "^10.0.27" + "@emotion/styled@^11": version "11.3.0" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.3.0.tgz#d63ee00537dfb6ff612e31b0e915c5cf9925a207" @@ -973,17 +1038,27 @@ "@emotion/serialize" "^1.0.2" "@emotion/utils" "^1.0.0" -"@emotion/unitless@^0.7.5": +"@emotion/stylis@0.8.5": + version "0.8.5" + resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== +"@emotion/utils@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" + integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== + "@emotion/utils@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== -"@emotion/weak-memoize@^0.2.5": +"@emotion/weak-memoize@0.2.5", "@emotion/weak-memoize@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== @@ -1849,6 +1924,18 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +apexcharts@^3.27.1: + version "3.27.1" + resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.27.1.tgz#b0e6dd3b3ace028f29b32fcd88e19a2420a18089" + integrity sha512-2pfw3pxeWhI0ap5lfxyfGNGoGScfEwfc8XnTpbnzgRdr1AOH5JJN9hh3MvfwrC9TQQfJYC2TZc8P/q9qXUj1bQ== + dependencies: + svg.draggable.js "^2.2.2" + svg.easing.js "^2.0.0" + svg.filter.js "^2.0.2" + svg.pathmorphing.js "^0.1.3" + svg.resize.js "^1.4.3" + svg.select.js "^3.0.1" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2028,6 +2115,22 @@ babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-plugin-emotion@^10.0.27: + version "10.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d" + integrity sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/serialize" "^0.11.16" + babel-plugin-macros "^2.0.0" + babel-plugin-syntax-jsx "^6.18.0" + convert-source-map "^1.5.0" + escape-string-regexp "^1.0.5" + find-root "^1.1.0" + source-map "^0.5.7" + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -2049,7 +2152,7 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^2.6.1: +babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.6.1: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -2058,7 +2161,7 @@ babel-plugin-macros@^2.6.1: cosmiconfig "^6.0.0" resolve "^1.12.0" -babel-plugin-syntax-jsx@6.18.0: +babel-plugin-syntax-jsx@6.18.0, babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= @@ -2707,6 +2810,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +csstype@^2.5.7: + version "2.6.17" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" + integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== + csstype@^3.0.2, csstype@^3.0.6: version "3.0.8" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" @@ -2731,6 +2839,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^2.22.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" + integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== + debug@2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5757,7 +5870,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@15.7.2, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@^15.5.7, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -5863,6 +5976,13 @@ raw-body@2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" +react-apexcharts@^1.3.9: + version "1.3.9" + resolved "https://registry.yarnpkg.com/react-apexcharts/-/react-apexcharts-1.3.9.tgz#d97e53fd513dc6ff73b90c2364c3bdd88d8dad01" + integrity sha512-KPonT5uQPHOHSVgTNEzpB0HhCkZtoicQYGjR9P+3DRDSgTsC+DM2vDUfo/B2Fn1m+wdgVeDXWL0VJYDc6JD/tw== + dependencies: + prop-types "^15.5.7" + react-clientside-effect@^1.2.2: version "1.2.5" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz#e2c4dc3c9ee109f642fac4f5b6e9bf5bcd2219a3" @@ -5879,6 +5999,14 @@ react-dom@17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-dropdown-select@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/react-dropdown-select/-/react-dropdown-select-4.7.4.tgz#d4467c3668cbc77825a96afd65194bd020c2b95e" + integrity sha512-3fp/Bz+Vl6t+NulfoU4NGGXAZ7zjpwsQdrvLN/IPrVZzcXSSwGSIubIESrLi9L0cl4SD7bqqQHERrp/CoiVFCw== + dependencies: + "@emotion/core" "^10.0.27" + "@emotion/styled" "^10.0.27" + react-error-boundary@^3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.3.tgz#276bfa05de8ac17b863587c9e0647522c25e2a0b" @@ -6743,6 +6871,61 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +svg.draggable.js@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz#c514a2f1405efb6f0263e7958f5b68fce50603ba" + integrity sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw== + dependencies: + svg.js "^2.0.1" + +svg.easing.js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/svg.easing.js/-/svg.easing.js-2.0.0.tgz#8aa9946b0a8e27857a5c40a10eba4091e5691f12" + integrity sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI= + dependencies: + svg.js ">=2.3.x" + +svg.filter.js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/svg.filter.js/-/svg.filter.js-2.0.2.tgz#91008e151389dd9230779fcbe6e2c9a362d1c203" + integrity sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM= + dependencies: + svg.js "^2.2.5" + +svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/svg.js/-/svg.js-2.7.1.tgz#eb977ed4737001eab859949b4a398ee1bb79948d" + integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA== + +svg.pathmorphing.js@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz#c25718a1cc7c36e852ecabc380e758ac09bb2b65" + integrity sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww== + dependencies: + svg.js "^2.4.0" + +svg.resize.js@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/svg.resize.js/-/svg.resize.js-1.4.3.tgz#885abd248e0cd205b36b973c4b578b9a36f23332" + integrity sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw== + dependencies: + svg.js "^2.6.5" + svg.select.js "^2.1.2" + +svg.select.js@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/svg.select.js/-/svg.select.js-2.1.2.tgz#e41ce13b1acff43a7441f9f8be87a2319c87be73" + integrity sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ== + dependencies: + svg.js "^2.2.5" + +svg.select.js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/svg.select.js/-/svg.select.js-3.0.1.tgz#a4198e359f3825739226415f82176a90ea5cc917" + integrity sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw== + dependencies: + svg.js "^2.6.5" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" From 021abe7fdafce05a1b9a413d8be4e9fc1e880087 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sun, 20 Jun 2021 20:16:36 -0400 Subject: [PATCH 30/33] modalRegisterStock --- src/components/ModalRegisterStock.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ModalRegisterStock.tsx b/src/components/ModalRegisterStock.tsx index 5afe010..dc04704 100644 --- a/src/components/ModalRegisterStock.tsx +++ b/src/components/ModalRegisterStock.tsx @@ -88,7 +88,7 @@ export function ModalRegisterStock({ } ); - const { isLoading, error, data } = useQuery('filterItems', () => + const { isLoading, error, data } = useQuery('getItems', () => api.get('/items').then(response => response.data) ); From 6411b89bfb53f826fba78b8708b4ce0740d01b91 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sun, 20 Jun 2021 20:38:36 -0400 Subject: [PATCH 31/33] refactoring: problem with list items query --- src/pages/stock.tsx | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index 2c415e9..a11f497 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -27,6 +27,7 @@ import { Sidebar } from '../components/Sidebar'; import { Button } from '../components/Button'; import { withSSRAuth } from '../utils/withSSRAuth'; import { api } from '../services/apiClient'; +import { setupApiClient } from '../services/api'; interface Stock { id: string; @@ -52,20 +53,15 @@ interface FilterFormData { filterByItem: string; } -export default function Stock(): JSX.Element { +interface StockProps { + listItems: Item[]; +} + +export default function Stock({ listItems }: StockProps): JSX.Element { const { isOpen, onClose, onOpen } = useDisclosure(); const { handleSubmit, register } = useForm(); const [filterItems, setFilterItems] = useState(''); - // const [stockListByItems, setStockListByItems] = useState({ - // stocks: [], - // item: null, - // }); - - const { data: listItems } = useQuery('getItems', () => - api.get('/items').then(response => response.data.items) - ); - const { isLoading, refetch, data } = useQuery( ['getStock', filterItems], () => @@ -80,7 +76,6 @@ export default function Stock(): JSX.Element { useEffect(() => { if (filterItems) { - console.log(filterItems); refetch(); } }, [filterItems, refetch]); @@ -131,11 +126,12 @@ export default function Stock(): JSX.Element { placeholder="Select option" onSelect={event => setFilterItems(event.currentTarget.value)} > - {listItems?.map(item => ( - - ))} + {listItems && + listItems.map(item => ( + + ))} @@ -206,8 +202,15 @@ export default function Stock(): JSX.Element { ); } -export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { - return { - props: {}, - }; -}); +export const getServerSideProps: GetServerSideProps = withSSRAuth( + async ctx => { + const apiClient = setupApiClient(ctx); + + const response = await apiClient.get('/items'); + return { + props: { + listItems: response.data.items, + }, + }; + } +); From 74218a99cdb170f4936e4837b6ad814c56a62b93 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sun, 20 Jun 2021 20:40:29 -0400 Subject: [PATCH 32/33] type: fix type props --- src/pages/stock.tsx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index a11f497..b19c742 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -202,15 +202,13 @@ export default function Stock({ listItems }: StockProps): JSX.Element { ); } -export const getServerSideProps: GetServerSideProps = withSSRAuth( - async ctx => { - const apiClient = setupApiClient(ctx); +export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { + const apiClient = setupApiClient(ctx); - const response = await apiClient.get('/items'); - return { - props: { - listItems: response.data.items, - }, - }; - } -); + const response = await apiClient.get('/items'); + return { + props: { + listItems: response.data.items, + }, + }; +}); From 199197aec2526dbc3d8596bdfd07ca76a645a8c3 Mon Sep 17 00:00:00 2001 From: Igor Cunha Date: Sun, 20 Jun 2021 21:15:04 -0400 Subject: [PATCH 33/33] refactoring: fix methodoly of get initial data --- src/pages/dashboard.tsx | 74 +++++++++++++++++++++++++++++------------ src/pages/stock.tsx | 4 +-- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 73bee9b..164bbf5 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -20,6 +20,7 @@ import { theme } from '../styles/theme'; import { Sidebar } from '../components/Sidebar'; import { api } from '../services/apiClient'; import { withSSRAuth } from '../utils/withSSRAuth'; +import { setupApiClient } from '../services/api'; interface ItemSummaryData { item: { @@ -39,6 +40,17 @@ interface ItemSummaryData { balance: number; } +interface Dashboard { + itemId: string; + itemName: string; + balance: number; + totalQtd: number; +} + +interface DashboardProps { + dashboard: Dashboard[]; +} + const Chart = dynamic(() => import('react-apexcharts'), { ssr: false, }); @@ -72,25 +84,27 @@ const options = { }, }; -export default function Dashboard(): JSX.Element { +export default function Dashboard({ + dashboard = [], +}: DashboardProps): JSX.Element { const [itemsSummary, setItemsSummary] = useState([]); const [chartData, setChartData] = useState([]); - const { isLoading, isError, error } = useQuery('getStock', () => - api.get('/stocks/dashboard').then(response => { - setItemsSummary(response.data); - setChartData( - response.data.map(is => { - return { - itemId: is.item.id, - itemName: is.item.name, - balance: is.balance, - totalQtd: is.totalQtd, - measureunity: is.item.measureunity, - }; - }) - ); - }) - ); + // const { isLoading, isError, error } = useQuery('getStock', () => + // api.get('/stocks/dashboard').then(response => { + // setItemsSummary(response.data); + // setChartData( + // response.data.map(is => { + // return { + // itemId: is.item.id, + // itemName: is.item.name, + // balance: is.balance, + // totalQtd: is.totalQtd, + // measureunity: is.item.measureunity, + // }; + // }) + // ); + // }) + // ); return ( @@ -118,10 +132,12 @@ export default function Dashboard(): JSX.Element { formatter: val => `R$ ${val}`, }, }, - xaxis: { categories: chartData.map(cd => cd.itemName) }, + xaxis: { + categories: dashboard.map(cd => cd.itemName), + }, }} series={[ - { name: 'Saldo', data: chartData.map(cd => cd.balance) }, + { name: 'Saldo', data: dashboard.map(cd => cd.balance) }, ]} type="bar" height={160} @@ -132,10 +148,10 @@ export default function Dashboard(): JSX.Element { cd.itemName) }, + xaxis: { categories: dashboard.map(cd => cd.itemName) }, }} series={[ - { name: 'Total', data: chartData.map(cd => cd.totalQtd) }, + { name: 'Total', data: dashboard.map(cd => cd.totalQtd) }, ]} type="bar" height={160} @@ -149,7 +165,21 @@ export default function Dashboard(): JSX.Element { } export const getServerSideProps: GetServerSideProps = withSSRAuth(async ctx => { + const apiClient = setupApiClient(ctx); + + const response = await apiClient.get('/stocks/dashboard'); + const dashboard = response.data.dashboardData.map(dash => { + return { + itemId: dash.item.id, + itemName: dash.item.name, + balance: dash.balance, + totalQtd: dash.totalQtd, + }; + }); + return { - props: {}, + props: { + dashboard, + }, }; }); diff --git a/src/pages/stock.tsx b/src/pages/stock.tsx index b19c742..fbe9d65 100644 --- a/src/pages/stock.tsx +++ b/src/pages/stock.tsx @@ -187,8 +187,8 @@ export default function Stock({ listItems }: StockProps): JSX.Element { /> )} - R$ {stock.quantity} - {stock.value} + {stock.quantity} + R$ {stock.value} ))}