├── .gitignore ├── README.md ├── components ├── Admin │ ├── CategoryForm │ │ └── index.tsx │ ├── CouponForm │ │ └── index.tsx │ ├── Dashboard │ │ ├── index.tsx │ │ └── styles.module.css │ ├── DashboardSalesRange │ │ ├── index.tsx │ │ └── styles.module.css │ ├── DashboardSummary │ │ ├── index.tsx │ │ └── styles.module.css │ ├── DashboardTopProducts │ │ ├── index.tsx │ │ └── styles.module.css │ ├── ProductForm │ │ ├── ProductImage │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ └── index.tsx │ ├── SystemRequirementForm │ │ └── index.tsx │ ├── UserForm │ │ └── index.tsx │ └── styles.module.css ├── LoginForm │ └── index.tsx ├── SignUpForm │ └── index.tsx ├── Storefront │ ├── CartModal │ │ ├── index.tsx │ │ └── styles.module.css │ ├── CartModalItem │ │ ├── index.tsx │ │ └── styles.module.css │ ├── CheckoutForm │ │ ├── index.tsx │ │ └── styles.module.css │ ├── HighlightedProducts │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Menu │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Product │ │ ├── index.tsx │ │ └── styles.module.css │ ├── ProductLicensesModal │ │ ├── index.tsx │ │ └── styles.module.css │ └── WishItem │ │ ├── index.tsx │ │ └── styles.module.css ├── shared │ ├── AdminComponent │ │ └── index.tsx │ ├── AdminDeleteModal │ │ ├── index.tsx │ │ └── styles.module.css │ ├── AdminFooter │ │ └── index.tsx │ ├── AdminHeader │ │ ├── index.tsx │ │ └── styles.module.css │ ├── AdminListTable │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Badge │ │ ├── index.tsx │ │ └── styles.module.css │ ├── BlueBackground │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Footer │ │ └── StorefrontFooter │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── Header │ │ └── StorefrontHeader │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── LateralMenu │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Logo │ │ ├── index.tsx │ │ └── styles.module.css │ ├── MainComponent │ │ └── index.tsx │ ├── NoData │ │ ├── index.tsx │ │ └── styles.module.css │ ├── OrderDetail │ │ ├── index.tsx │ │ └── styles.module.css │ ├── OrdersList │ │ ├── index.tsx │ │ └── styles.module.css │ ├── Pagination │ │ └── index.tsx │ ├── PasswordComponent │ │ └── index.tsx │ ├── ProductInfo │ │ ├── index.tsx │ │ └── styles.module.css │ ├── StyledButton │ │ ├── index.tsx │ │ └── styles.module.css │ └── TitleAdminPanel │ │ ├── SearchAndIcon │ │ ├── index.tsx │ │ └── styles.module.css │ │ ├── TitleAndPath │ │ ├── index.tsx │ │ └── styles.module.css │ │ ├── index.tsx │ │ └── styles.module.css ├── withAuth │ └── index.tsx └── withAuthAdmin │ └── index.tsx ├── dtos ├── ApiData.ts ├── ApiResponseError.ts ├── AuthState.ts ├── Category.ts ├── Checkout.ts ├── CheckoutItem.ts ├── Coupon.ts ├── Dashboard.ts ├── Game.ts ├── HomeIndexData.ts ├── Meta.ts ├── Order.ts ├── OrderItem.ts ├── OrdersList.ts ├── Product.ts ├── ProductHome.ts ├── ProductSearch.ts ├── ProductShow.ts ├── ProductShowData.ts ├── SystemRequirement.ts └── User.ts ├── next-env.d.ts ├── package-lock.json ├── package.json ├── pages ├── Admin │ ├── Categories │ │ ├── Edit │ │ │ └── index.tsx │ │ ├── List │ │ │ └── index.tsx │ │ └── New │ │ │ └── index.tsx │ ├── Coupons │ │ ├── Edit │ │ │ └── index.tsx │ │ ├── List │ │ │ └── index.tsx │ │ └── New │ │ │ └── index.tsx │ ├── Orders │ │ ├── List │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ ├── [id].tsx │ │ └── styles.module.css │ ├── Products │ │ ├── Edit │ │ │ └── index.tsx │ │ ├── List │ │ │ └── index.tsx │ │ └── New │ │ │ └── index.tsx │ ├── SystemRequirements │ │ ├── Edit │ │ │ └── index.tsx │ │ ├── List │ │ │ └── index.tsx │ │ └── New │ │ │ └── index.tsx │ ├── Users │ │ ├── Edit │ │ │ └── index.tsx │ │ ├── List │ │ │ └── index.tsx │ │ └── New │ │ │ └── index.tsx │ └── index.tsx ├── Auth │ ├── ChangePassword │ │ └── index.tsx │ ├── Login │ │ └── index.tsx │ └── PasswordRecovery │ │ └── index.tsx ├── Cart │ ├── index.tsx │ └── styles.module.css ├── Games │ └── index.tsx ├── Orders │ ├── List │ │ └── index.tsx │ ├── [id].tsx │ └── styles.module.css ├── PaymentConfirmation │ ├── [id].tsx │ └── styles.module.css ├── Product │ ├── [id].tsx │ └── styles.module.css ├── Profile │ ├── index.tsx │ └── styles.module.css ├── Search │ ├── index.tsx │ └── styles.module.css ├── Wishlist │ └── index.tsx ├── _app.tsx ├── index.tsx └── styles.module.css ├── public ├── assets │ ├── card-flags │ │ ├── boleto.png │ │ ├── mastercard.png │ │ └── visa.png │ ├── logo-bootcamp.png │ ├── logo-games.png │ └── product_image.png ├── favicon.ico ├── snippets │ ├── 1-criando_nosso_app.md │ ├── 10-criando_menu_lateral.md │ ├── 11-criando_lista_de_usuarios(admin).md │ ├── 12-criando_detalhes_do_usuario(admin).md │ ├── 13-criando_pagina_para_adicionar_user.md │ ├── 14-criando_pagina_para_editar_usuario.md │ ├── 15-criando_list_de_produtos.md │ ├── 16-criando-details_do_produto.md │ ├── 17-criando_novo_produto.md │ ├── 18-criando_pagina_para_editar_produto.md │ ├── 19-criando_list_de_categorias.md │ ├── 2-instalando_o_bootstrap.md │ ├── 20-criando_details_da_categoria.md │ ├── 21-criando_edit_de_categoria.md │ ├── 22-criando_new_de_categorias.md │ ├── 3-criando_nosso_header.md │ ├── 4-criando_nosso_footer.md │ ├── 5-criando-formulario-pagina-inicial.md │ ├── 6-criando_recuperacao_de_senha.md │ ├── 7-criando_modificacao_de_senha.md │ ├── 8-criando_header_admin.md │ └── 9-criando_footer_admin.md └── vercel.svg ├── services ├── adminOrders.ts ├── api.ts ├── categories.ts ├── checkout.ts ├── coupons.ts ├── dashboardSalesRange.ts ├── dashboardSumarry.ts ├── dashboardTopProducts.ts ├── games.ts ├── home.ts ├── order.ts ├── productShow.ts ├── products.ts ├── profile.ts ├── search.ts ├── systemRequirements.ts ├── users.ts ├── validateCoupon.ts └── wishlist.ts ├── store ├── index.ts └── modules │ ├── admin │ ├── category │ │ └── reducer.ts │ ├── coupon │ │ └── reducer.ts │ ├── dashboard │ │ └── reducer.ts │ ├── product │ │ └── reducer.ts │ ├── shared │ │ └── search │ │ │ └── reducer.ts │ ├── systemRequirement │ │ └── reducer.ts │ └── user │ │ └── reducer.ts │ ├── auth │ └── reducer.ts │ ├── rootReducer.ts │ └── storefront │ └── cartProducts │ └── reducer.ts ├── styles └── globals.css ├── tsconfig.json ├── util ├── AggregateItemsService.ts ├── JunoService.ts ├── LoggedService.ts ├── MonthsService.ts ├── OrderStatusService.ts ├── PaginationService.ts ├── ProductSearchService.ts ├── SignOutService.ts └── UrlService.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on Vercel 27 | 28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /components/Admin/CategoryForm/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | import { Form } from 'react-bootstrap'; 4 | import { faGhost, faTimes } from '@fortawesome/free-solid-svg-icons'; 5 | import StyledButton from '../../../components/shared/StyledButton'; 6 | 7 | import { useRouter } from 'next/router'; 8 | import { useSelector, useDispatch } from 'react-redux'; 9 | 10 | import styles from '../styles.module.css'; 11 | import Category from '../../../dtos/Category'; 12 | 13 | import { clearCategoryToEdit } from '../../../store/modules/admin/category/reducer'; 14 | 15 | // para que possamos reutilizar o form, necessitaremos de receber o método que será executado quando form for submetido (um para a criação e outro para a atualização da categoria) e também o texto do botão de confirmação (action) que é opcional 16 | interface CategoryFormProps { 17 | handleSubmit: (category: Category) => Promise; 18 | action?: string; 19 | } 20 | 21 | 22 | const CategoryForm: React.FC = ({ handleSubmit, action = 'Adicionar' }) => { 23 | const [name, setName] = useState(''); 24 | 25 | // aqui obetmos a categoria que estiver armazenada na store do redux para podermos pegar os dados para edição 26 | const category = useSelector(state => state.category); 27 | 28 | const dispatch = useDispatch(); 29 | 30 | // checando se a categoria não é vazia e se o a url contem a palavra Edit para setar o valor do nome para a edição. 31 | useEffect(() => { 32 | if(category && router.pathname.includes('Edit')) { 33 | setName(category.name); 34 | } 35 | }, [category]); 36 | 37 | const router = useRouter(); 38 | 39 | // quando o form for submetido, prevenimos a operação normal do form que seria dar um refresh na página e chamamos o método que foi recebido por parâmetro enviando um objeto do tipo Category 40 | const handleFormSubmit = async (evt: React.FormEvent): Promise => { 41 | evt.preventDefault(); 42 | 43 | // como o id não é um campo visível, pegamos o mesmo da categoria que foi armazenada na store do redux, se a mesma for nula, nulo / undefined é retornado (?., evita termos que faze um if para realizar uma checagem) 44 | await handleSubmit({ id: category?.id, name }); 45 | } 46 | 47 | return ( 48 |
49 |
50 | Nome 51 | ) => 58 | setName(evt.target.value) 59 | } 60 | required 61 | /> 62 | 63 |
64 | 70 | 71 | { 76 | // limpando a categoria para edição quando a edição é cancelada para não enviar o id caso seja um cadastro para não dar erro de chave primária 77 | dispatch(clearCategoryToEdit()); 78 | router.back(); 79 | }} 80 | /> 81 |
82 | 83 |
84 | ) 85 | } 86 | 87 | export default CategoryForm; -------------------------------------------------------------------------------- /components/Admin/Dashboard/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Row, Col } from "react-bootstrap"; 3 | import styles from './styles.module.css'; 4 | 5 | import DashboardSummary from '../DashboardSummary'; 6 | import DashboardGraphic from "../DashboardSalesRange"; 7 | import DashboardTopProducts from "../DashboardTopProducts"; 8 | 9 | import { useDispatch } from 'react-redux'; 10 | import { updateDates } from '../../../store/modules/admin/dashboard/reducer'; 11 | import { addDays, format } from "date-fns"; 12 | 13 | const Dashboard: React.FC = () => { 14 | const [minDate, setMinDate] = useState(''); 15 | const [maxDate, setMaxDate] = useState(''); 16 | const dispatch = useDispatch(); 17 | 18 | const handleSetDates = (days: number) => { 19 | setMinDate(format(addDays(new Date(), - days), 'yyyy-MM-dd')); 20 | setMaxDate(format(new Date(), 'yyyy-MM-dd')); 21 | } 22 | 23 | useEffect(() => { 24 | dispatch(updateDates({ min_date: minDate, max_date: maxDate })); 25 | }, [minDate, maxDate]) 26 | 27 | return ( 28 | <> 29 |
30 | 31 | 32 |

Página inicial

33 | Dashboard / Painel Inicial 34 | 35 | 36 | 37 |
38 | 41 | 44 | 47 |
48 | 49 |
50 | de 51 | ) => 56 | setMinDate(evt.currentTarget.value) 57 | } 58 | /> 59 | até 60 | ) => 65 | setMaxDate(evt.currentTarget.value) 66 | } 67 | /> 68 |
69 | 70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | export default Dashboard; -------------------------------------------------------------------------------- /components/Admin/Dashboard/styles.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | margin: 20px 0 10px 0; 3 | text-align: left; 4 | } 5 | 6 | .header div:nth-of-type(2) { 7 | display: flex; 8 | height: 20px; 9 | align-items: center; 10 | justify-content: flex-end; 11 | flex-wrap: wrap; 12 | } 13 | 14 | .header div:nth-of-type(2) div:first-of-type { 15 | margin-right: 5px; 16 | border-right: solid 1px rgba(255, 255, 255, 0.2); 17 | } 18 | 19 | .header h1 { 20 | font-size: 1em; 21 | font-weight: bold; 22 | display: inline-block; 23 | margin-right: 15px; 24 | text-align: left; 25 | } 26 | 27 | .header input { 28 | display: inline-block; 29 | width: 120px; 30 | margin: 0 5px; 31 | font-size: 0.65em; 32 | font-weight: bold; 33 | background: var(--color-primary); 34 | color: white; 35 | outline: none; 36 | border: none; 37 | } 38 | 39 | .header span { 40 | font-size: 0.75em; 41 | font-weight: bold; 42 | } 43 | 44 | .header button { 45 | color: white; 46 | background: var(--color-primary); 47 | outline: none; 48 | border: none; 49 | font-size: 0.8em; 50 | font-weight: bold; 51 | margin: 0 5px; 52 | transition: background 0.2s; 53 | } 54 | 55 | .header button:hover { 56 | background: var(--color-hover); 57 | } 58 | 59 | .header button:active { 60 | background: var(--color-active); 61 | } 62 | 63 | @media screen and (max-width: 1213px) { 64 | .header div:nth-of-type(2) div:first-of-type { 65 | border-right: none; 66 | } 67 | } 68 | 69 | @media screen and (max-width: 1211px) { 70 | .header { 71 | margin-bottom: 30px; 72 | } 73 | } 74 | 75 | @media screen and (max-width: 991px) { 76 | .header { 77 | margin: 5px 0 35px 0; 78 | text-align: center; 79 | } 80 | 81 | .header div:nth-of-type(2) { 82 | display: block; 83 | text-align: center; 84 | padding: 0; 85 | } 86 | 87 | .header div:nth-of-type(2) div:last-of-type { 88 | display: block; 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /components/Admin/DashboardSalesRange/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.css'; 2 | import { LineChart, Line, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'; 3 | import DashboardSalesRangeService from '../../../services/dashboardSalesRange'; 4 | import useSwr from 'swr'; 5 | import { toast } from 'react-toastify'; 6 | 7 | import { useSelector } from 'react-redux'; 8 | import Dashboard from '../../../dtos/Dashboard'; 9 | 10 | const defaultUrl = '/admin/v1/dashboard/sales_ranges'; 11 | 12 | const DashboardGraphic: React.FC = () => { 13 | const { min_date, max_date }: Dashboard = useSelector(state => state.dashboard); 14 | 15 | const { data, error } = useSwr( 16 | () => defaultUrl + 17 | ((min_date || max_date) ? 18 | `?min_date=${min_date}&max_date=${max_date}` : ''), 19 | DashboardSalesRangeService.index 20 | ); 21 | 22 | if (error) { 23 | toast.error('Erro ao obter os dados para o gráfico do dashboard.') 24 | console.log(error); 25 | } 26 | 27 | return ( 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | ( [`R$ ${value.toFixed(2)}`, "Total vendido"]) 42 | } 43 | /> 44 | 45 | 46 |
47 | ); 48 | } 49 | 50 | export default DashboardGraphic; -------------------------------------------------------------------------------- /components/Admin/DashboardSalesRange/styles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 425px; 3 | text-align: left; 4 | font-weight: bold; 5 | background: var(--color-primary); 6 | padding: 15px; 7 | border-radius: 5px; 8 | } -------------------------------------------------------------------------------- /components/Admin/DashboardSummary/index.tsx: -------------------------------------------------------------------------------- 1 | import { Col, Row } from "react-bootstrap"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faUser, faGamepad, faShoppingCart, faDollarSign } from "@fortawesome/free-solid-svg-icons"; 4 | 5 | import styles from './styles.module.css'; 6 | import useSwr from 'swr'; 7 | import DashboardSummaryService from "../../../services/dashboardSumarry"; 8 | import { toast } from 'react-toastify'; 9 | 10 | import { useSelector } from 'react-redux'; 11 | import Dashboard from "../../../dtos/Dashboard"; 12 | 13 | const defaultUrl = '/admin/v1/dashboard/summaries'; 14 | 15 | const DashboardSummary: React.FC = () => { 16 | const { min_date, max_date }: Dashboard = useSelector(state => state.dashboard); 17 | 18 | const { data, error } = useSwr( 19 | () => defaultUrl + 20 | ((min_date || max_date) ? 21 | `?min_date=${min_date}&max_date=${max_date}` : ''), 22 | DashboardSummaryService.index 23 | ); 24 | 25 | if (error) { 26 | toast.error('Erro ao obter os dados para o resumo do dashboard.'); 27 | console.log(error); 28 | } 29 | 30 | return ( 31 | 32 | 33 |
34 | 35 |
36 | + {data?.users} 37 | Usuários 38 |
39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 | + {data?.products} 47 | Produtos 48 |
49 |
50 | 51 | 52 | 53 |
54 | 55 |
56 | + {data?.orders} 57 | Vendas 58 |
59 |
60 | 61 | 62 | 63 |
64 | 65 |
66 | R$ {data?.profit} 67 | Lucro Total 68 |
69 |
70 | 71 |
72 | ); 73 | } 74 | 75 | export default DashboardSummary; -------------------------------------------------------------------------------- /components/Admin/DashboardSummary/styles.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | display: flex; 3 | justify-content: space-evenly; 4 | align-items: center; 5 | padding: 10px; 6 | 7 | height: 60px; 8 | border: solid 2px white; 9 | border-radius: 5px; 10 | } 11 | 12 | .card div span { 13 | display: block; 14 | font-weight: bold; 15 | text-align: left; 16 | font-size: 0.9em; 17 | } 18 | 19 | .card div span:first-of-type { 20 | font-size: 1.3em; 21 | } 22 | 23 | @media screen and (max-width: 1085px) { 24 | .card { 25 | text-align: center; 26 | margin: 10px 0; 27 | } 28 | 29 | .card div span:first-of-type { 30 | font-size: 0.8em; 31 | } 32 | 33 | .card svg { 34 | font-size: 1em; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /components/Admin/DashboardTopProducts/index.tsx: -------------------------------------------------------------------------------- 1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 2 | import { faShoppingCart, faDollarSign } from "@fortawesome/free-solid-svg-icons"; 3 | 4 | import styles from './styles.module.css'; 5 | import useSwr from 'swr'; 6 | import DashboardTopProductService from "../../../services/dashboardTopProducts"; 7 | import { toast } from "react-toastify"; 8 | 9 | import { useSelector } from 'react-redux'; 10 | import Dashboard from "../../../dtos/Dashboard"; 11 | 12 | const defaultUrl = '/admin/v1/dashboard/top_five_products'; 13 | 14 | const DashboardTopProducts: React.FC = () => { 15 | const { min_date, max_date }: Dashboard = useSelector(state => state.dashboard); 16 | 17 | const { data, error } = useSwr( 18 | () => defaultUrl + 19 | ((min_date || max_date) ? 20 | `?min_date=${min_date}&max_date=${max_date}` : ''), 21 | DashboardTopProductService.index 22 | ); 23 | 24 | if (error) { 25 | toast.error('Erro ao obter os dados para os top 5 produtos.'); 26 | console.log(error); 27 | } 28 | 29 | return ( 30 |
31 |

Top 5 mais vendidos

32 | 33 | { 34 | data?.map( 35 | (product, index) => 36 |
37 | {product?.product} 38 | 39 |
40 |
41 | {product?.product} 42 |
43 | 44 |
45 | 46 | 47 | {product?.quantity} 48 | 49 | 50 | 51 | 52 | {product?.total_sold} 53 | 54 |
55 |
56 |
57 | ) 58 | } 59 |
60 | ); 61 | } 62 | 63 | export default DashboardTopProducts; -------------------------------------------------------------------------------- /components/Admin/DashboardTopProducts/styles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 425px; 3 | text-align: left; 4 | font-weight: bold; 5 | background: var(--color-primary); 6 | padding: 15px; 7 | border-radius: 5px; 8 | } 9 | 10 | .product { 11 | display: flex; 12 | align-items: top; 13 | flex-wrap: wrap; 14 | margin: 20px 0; 15 | } 16 | 17 | .product div:first-of-type { 18 | height: 25px; 19 | flex: 1; 20 | } 21 | 22 | .product div div:nth-of-type(2) { 23 | text-align: right; 24 | flex: 1 0; 25 | } 26 | 27 | .product img { 28 | width: 90px; 29 | height: 50px; 30 | margin-right: 5px; 31 | } 32 | 33 | .product div div:nth-of-type(2) span { 34 | display: inline-block; 35 | font-size: 0.7em; 36 | font-weight: bold; 37 | border: 1.5px solid white; 38 | border-radius: 2.5px; 39 | padding: 0 5px; 40 | margin-left: 10px; 41 | } 42 | 43 | .product div div:nth-of-type(2) svg { 44 | margin-right: 5px; 45 | } 46 | 47 | .product div:nth-of-type(2) span:nth-of-type(1) { 48 | border: 1.5px solid var(--color-active); 49 | } 50 | 51 | @media screen and (max-width: 1100px) { 52 | .product div div:first-of-type { 53 | max-width: 140px; 54 | overflow: hidden; 55 | white-space: nowrap; 56 | text-overflow: ellipsis; 57 | } 58 | } -------------------------------------------------------------------------------- /components/Admin/ProductForm/ProductImage/index.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, useState, useEffect, Dispatch, SetStateAction } from 'react'; 2 | import { Col } from 'react-bootstrap'; 3 | import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; 4 | import StyledButton from '../../../shared/StyledButton'; 5 | 6 | import styles from './styles.module.css'; 7 | 8 | // estamos recebendo a função de setImage que virá do ProductForm, necessitamos dela pois vamos mandar o arquivo para a api e para exibir a imagem na tela utilizaremos o método Url.createObjectUrl que irá criar uma url fictícia para a imagem e assim a mesma será exibida na tela. 9 | // já o productImage será utilizado para exibir a image que foi recebida da api na edição do produto 10 | interface ProductImageProps { 11 | setImage: Dispatch>; 12 | productImage: string; 13 | } 14 | 15 | const ProductImage: React.FC = ({ setImage, productImage }) => { 16 | // esse estado é utilizado para armazenar a url da imagem que será exibida na tela 17 | const [imageToShow, setImageToShow] = useState('/assets/product_image.png'); 18 | // utilizarames um ref hook do react para podermos ter acesso mais fácil ao input do tipo file que esta escondido na tela. 19 | const imageInputRef = useRef(null); 20 | 21 | // se for uma edição e imagem do produto existir, a imagem será alterada para a imagem do produto 22 | useEffect (() => { 23 | console.log(productImage) 24 | if (productImage) { 25 | setImageToShow(productImage); 26 | } 27 | }, [productImage]) 28 | 29 | // utilizando um ref para simular o click do botão do input type="file" quando o botão atualizar é clicado 30 | const handleUpdateImage = (): void => { 31 | if (imageInputRef) { 32 | imageInputRef.current.click(); 33 | } 34 | } 35 | 36 | // pegando a imagem que foi selecionada, setando ela no estado do componente pai e criando uma url para que a mesma seja exibida na tela 37 | const handleSetImage = (evt: React.ChangeEvent) => { 38 | const file = evt.target.files[0]; 39 | 40 | setImage(file); 41 | setImageToShow(URL.createObjectURL(file)) 42 | } 43 | 44 | return ( 45 | 46 | 69 | 70 |
71 | 77 |
78 | 79 | ); 80 | } 81 | 82 | export default ProductImage; -------------------------------------------------------------------------------- /components/Admin/ProductForm/ProductImage/styles.module.css: -------------------------------------------------------------------------------- 1 | .image_label { 2 | display: flex; 3 | align-items: center; 4 | margin-bottom: 20px; 5 | } 6 | 7 | .image { 8 | max-width: 240px; 9 | max-height: 200px; 10 | display: block; 11 | margin-left: auto; 12 | margin-right: auto; 13 | cursor: pointer; 14 | } 15 | 16 | .details_button { 17 | text-align: right; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | margin-bottom: 20px; 22 | } -------------------------------------------------------------------------------- /components/Admin/styles.module.css: -------------------------------------------------------------------------------- 1 | .admin_panel { 2 | background-color: var(--color-secondary); 3 | padding: 30px; 4 | border-radius: 10px; 5 | margin-top: 20px; 6 | } 7 | 8 | .details_button { 9 | text-align: right; 10 | } 11 | 12 | .details_button > button { 13 | margin: 10px; 14 | } 15 | 16 | .new_form { 17 | text-align: left; 18 | padding: 20px; 19 | } 20 | 21 | .secundary_input, .secundary_input:focus { 22 | background-color: var(--color-background); 23 | color: white; 24 | border: none; 25 | } -------------------------------------------------------------------------------- /components/LoginForm/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from 'react'; 2 | import { useRouter } from 'next/router'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { InputGroup, FormControl, Button, Row, Col } from 'react-bootstrap'; 5 | import BlueBackground from '../shared/BlueBackground'; 6 | 7 | import { setLoggedUser } from '../../store/modules/auth/reducer'; 8 | 9 | import Link from 'next/link'; 10 | 11 | import UsersService from '../../services/users'; 12 | 13 | import { toast } from 'react-toastify'; 14 | 15 | import AuthState from '../../dtos/AuthState'; 16 | import User from '../../dtos/User'; 17 | 18 | interface LoginProps { 19 | titlePhrase: string; 20 | buttonPhrase: string; 21 | } 22 | 23 | const LoginForm: React.FC = ({ titlePhrase, buttonPhrase }) => { 24 | const [email, setEmail] = useState(''); 25 | const [password, setPassword] = useState(''); 26 | const passwordRef = useRef(null); 27 | 28 | const loggedUser: User = useSelector((state: AuthState) => state.auth.loggedUser); 29 | 30 | const router = useRouter(); 31 | const dispatch = useDispatch(); 32 | 33 | useEffect(() => { 34 | if(loggedUser) { 35 | setEmail(loggedUser.email); 36 | if(passwordRef && passwordRef.current) { 37 | passwordRef.current.focus(); 38 | } 39 | } 40 | }, [loggedUser]) 41 | 42 | const handleSubmit = async (evt: React.FormEvent): Promise => { 43 | evt.preventDefault(); 44 | 45 | try { 46 | const response = await UsersService.signIn({ email, password }); 47 | 48 | const { id, email: userEmail, name, profile } = response.data.data; 49 | 50 | const user = { 51 | id, 52 | name, 53 | email: userEmail, 54 | profile: profile 55 | }; 56 | 57 | dispatch(setLoggedUser(user)); 58 | 59 | toast.info('Login realizado com sucesso!'); 60 | 61 | if (router.query.callback) { 62 | router.push(decodeURIComponent(router.query.callback.toString())); 63 | } else { 64 | router.push(user.profile === 'admin' ? '/Admin' : '/') 65 | } 66 | } catch (err) { 67 | toast.error('E-mail ou senha inválidos!'); 68 | } 69 | } 70 | return ( 71 | 72 |
73 | 74 | 75 | 76 |

{titlePhrase}

77 | 78 | 79 | 80 | ) => 86 | setEmail(evt.target.value) 87 | } 88 | required 89 | /> 90 | 91 | 92 | 93 | ) => 99 | setPassword(evt.target.value) 100 | } 101 | required 102 | ref={passwordRef} 103 | /> 104 | 105 | 106 | 107 | 108 |
109 | 110 | Esqueci minha senha
111 |
112 | 113 |
114 |
115 | ) 116 | } 117 | 118 | export default LoginForm; -------------------------------------------------------------------------------- /components/Storefront/CartModal/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.css'; 2 | import CartModalItem from '../CartModalItem'; 3 | import StyledButton from '../../shared/StyledButton'; 4 | import { faShoppingCart } from '@fortawesome/free-solid-svg-icons'; 5 | 6 | import { useSelector, useDispatch } from 'react-redux'; 7 | import { removeCartProduct } from '../../../store/modules/storefront/cartProducts/reducer'; 8 | import ProductShow from '../../../dtos/ProductShow'; 9 | import { useRouter } from 'next/router'; 10 | 11 | interface CartModalProps { 12 | searchPage?: boolean; 13 | } 14 | 15 | const CartModal: React.FC = ({searchPage = false}) => { 16 | const cartProducts: ProductShow[] = useSelector(state => state.cartProducts); 17 | const dispatch = useDispatch(); 18 | const router = useRouter(); 19 | 20 | const handleRemove = (index: number): void => { 21 | dispatch(removeCartProduct(index)); 22 | } 23 | 24 | return ( 25 |
26 |
27 | { 28 | cartProducts?.map( 29 | (product, index) => 30 | handleRemove(index)} 34 | /> 35 | ) 36 | } 37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 | { 45 | `R$ ${cartProducts?.reduce((acc, item) => acc + item.price, 0).toFixed(2)}` 46 | } 47 | 48 | 49 | router.push('/Cart')} 55 | /> 56 |
57 |
58 | ); 59 | } 60 | 61 | export default CartModal; -------------------------------------------------------------------------------- /components/Storefront/CartModal/styles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | width: 320px; 4 | max-height: 450px; 5 | border-radius: 20px; 6 | background: white; 7 | z-index: 999; 8 | color: black; 9 | left: -11em; 10 | padding-bottom: 5px; 11 | margin: 20px -246.08px -455px auto; 12 | } 13 | 14 | .container::before { 15 | content: ''; 16 | display: block; 17 | width: 25px; 18 | height: 25px; 19 | position: relative; 20 | top: -28px; 21 | margin-left: auto; 22 | margin-right: 32px; 23 | margin-bottom: -10px; 24 | border: 15px solid transparent; 25 | border-bottom: 15px solid white; 26 | } 27 | 28 | .items { 29 | max-height: 350px; 30 | overflow-x: hidden; 31 | overflow-y: auto; 32 | } 33 | 34 | .separator { 35 | content: ''; 36 | display: block; 37 | width: 90%; 38 | height: 1px; 39 | background: black; 40 | margin: 15px auto; 41 | } 42 | 43 | .actions { 44 | margin-bottom: 10px; 45 | padding: 0 15px; 46 | display: flex; 47 | justify-content: space-between; 48 | align-items: center; 49 | } 50 | 51 | .actions span { 52 | font-size: 0.75em; 53 | font-weight: 600; 54 | } 55 | 56 | .actions button { 57 | font-weight: 600; 58 | font-size: 0.65em; 59 | } 60 | 61 | @media screen and (max-width: 767px) { 62 | .container { 63 | left: -5.8em; 64 | width: 305px; 65 | } 66 | 67 | .container::before { 68 | margin: 0 auto 0 42%; 69 | } 70 | 71 | .search_page { 72 | left: -2.35em; 73 | } 74 | 75 | .search_page::before { 76 | margin: 0 0 0 2.35em; 77 | } 78 | } -------------------------------------------------------------------------------- /components/Storefront/CartModalItem/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.css'; 2 | import { Row, Col, Badge } from 'react-bootstrap'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faTrash } from '@fortawesome/free-solid-svg-icons'; 5 | 6 | import ProductShow from '../../../dtos/ProductShow'; 7 | import { MouseEventHandler } from 'react'; 8 | 9 | interface CartModalItemProps { 10 | product: ProductShow; 11 | handleRemove: MouseEventHandler; 12 | } 13 | 14 | const CartModalItem: React.FC = ({product, handleRemove}) => { 15 | return ( 16 |
17 | 18 | 19 | {product.name} 23 | 24 | 25 | 26 |

27 | {product.name} 28 |

29 | 30 |
31 | { 32 | product?.categories?.filter((_, index) => index < 2)?.map( 33 | category => 34 | 39 | {category.name} 40 | 41 | ) 42 | } 43 |
44 | 45 |
46 | {`R$ ${product.price}`} 47 | 51 |
52 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default CartModalItem; -------------------------------------------------------------------------------- /components/Storefront/CartModalItem/styles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 270px; 3 | text-align: left; 4 | margin: 0 auto; 5 | margin-bottom: 10px; 6 | } 7 | 8 | .container img { 9 | width: 100%; 10 | height: 70px; 11 | border-radius: 10px; 12 | } 13 | 14 | .badges_container { 15 | margin-top: -10px; 16 | min-height: 33px; 17 | } 18 | 19 | .badge { 20 | background: var(--color-secondary); 21 | font-size: 0.5em; 22 | } 23 | 24 | .price_container { 25 | display: flex; 26 | justify-content: space-between; 27 | align-items: center; 28 | } 29 | 30 | .price_container span { 31 | font-size: 0.7em; 32 | } 33 | 34 | .price_container svg { 35 | color: var(--color-gray-light); 36 | font-size: 0.8em; 37 | } 38 | 39 | .container p { 40 | font-size: 0.7em; 41 | font-weight: 600; 42 | max-width: 250px; 43 | margin-bottom: 0px; 44 | } -------------------------------------------------------------------------------- /components/Storefront/CheckoutForm/styles.module.css: -------------------------------------------------------------------------------- 1 | .gray_input { 2 | background-color: var(--color-secondary); 3 | border: none; 4 | padding: 5px; 5 | margin-right: 10px; 6 | border-radius: 5px; 7 | color: white; 8 | } 9 | 10 | .gray_select { 11 | background-color: var(--color-secondary); 12 | border: none; 13 | padding: 5px; 14 | margin-right: 10px; 15 | border-radius: 5px; 16 | color: white; 17 | width: 100%; 18 | } 19 | 20 | .gray_button { 21 | background-color: var(--color-secondary); 22 | border: none; 23 | } 24 | 25 | .payment div { 26 | margin-right: 5px !important; 27 | margin-top: 5px !important; 28 | } 29 | 30 | .line { 31 | background-color: white; 32 | opacity: 0.2; 33 | } 34 | -------------------------------------------------------------------------------- /components/Storefront/HighlightedProducts/index.tsx: -------------------------------------------------------------------------------- 1 | import { Row, Col, Button } from 'react-bootstrap'; 2 | import styles from './styles.module.css'; 3 | import ProductInfo from '../../shared/ProductInfo'; 4 | 5 | import ProductHome from '../../../dtos/ProductHome'; 6 | 7 | interface HightlightedProductsProps { 8 | title: string; 9 | type?: string; 10 | products: ProductHome[]; 11 | handleSeeMore(event: React.MouseEvent): void; 12 | } 13 | 14 | const HightlightedProducts: React.FC = ({ title, type, products, handleSeeMore }) => { 15 | return ( 16 |
17 | 18 |
{title}
19 | 20 |
21 | 22 | 30 |
31 | 32 | 33 | { 34 | products?.map( 35 | product => ( 36 | 37 | 41 | 42 | ) 43 | ) 44 | } 45 | 46 | 47 |
48 | ) 49 | } 50 | 51 | export default HightlightedProducts; -------------------------------------------------------------------------------- /components/Storefront/HighlightedProducts/styles.module.css: -------------------------------------------------------------------------------- 1 | .products { 2 | margin-top: 20px; 3 | } 4 | 5 | .products_header { 6 | padding: 0px 15px; 7 | display: flex; 8 | align-items: center; 9 | justify-content: space-between; 10 | } 11 | 12 | .products_header h5 { 13 | margin: 0; 14 | } 15 | 16 | .line { 17 | background: white; 18 | height: 0.01rem; 19 | width: 60%; 20 | } 21 | 22 | @media screen and (max-width: 991px) { 23 | .products_header h5 { 24 | font-size: 1.1em; 25 | } 26 | 27 | .line { 28 | width: 50%; 29 | } 30 | 31 | .product_header button { 32 | font-size: 1em; 33 | } 34 | } 35 | 36 | .normal_button { 37 | text-transform: uppercase; 38 | font-weight: 600; 39 | font-size: 0.9em; 40 | background: var(--color-secondary); 41 | outline: none; 42 | border: 0; 43 | border-radius: 2px; 44 | padding: 5px 20px; 45 | } 46 | 47 | .highlighted_button { 48 | text-transform: uppercase; 49 | font-weight: 600; 50 | font-size: 0.9em; 51 | background: transparent; 52 | border-radius: 2px; 53 | padding: 5px 20px; 54 | border-width: 1.5px; 55 | border-color: var(--color-active); 56 | } 57 | 58 | .highlighted_button:hover, 59 | .highlighted_button:focus { 60 | background: var(--color-active); 61 | border-color: var(--color-active); 62 | color: black; 63 | } 64 | 65 | @media screen and (max-width: 767px) { 66 | .line { 67 | width: 35%; 68 | } 69 | } 70 | 71 | @media screen and (max-width: 550px) { 72 | .product_header h5 { 73 | font-size: 1em; 74 | } 75 | 76 | .line { 77 | width: 30%; 78 | } 79 | 80 | .product_header button { 81 | font-size: 0.8em; 82 | } 83 | } 84 | 85 | @media screen and (max-width: 384px) { 86 | .line { 87 | width: 15%; 88 | } 89 | } -------------------------------------------------------------------------------- /components/Storefront/Menu/index.tsx: -------------------------------------------------------------------------------- 1 | import { Col, Row } from 'react-bootstrap'; 2 | 3 | import styles from './styles.module.css'; 4 | import Link from 'next/link'; 5 | 6 | interface MenuProps { 7 | tab?: string; 8 | } 9 | 10 | const Menu: React.FC = ({ tab }) => { 11 | return ( 12 | 13 | 14 | 15 | 16 | Meus Dados 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Meus Pedidos 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Meus Games 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Games Desejados 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | 48 | export default Menu; -------------------------------------------------------------------------------- /components/Storefront/Menu/styles.module.css: -------------------------------------------------------------------------------- 1 | .container a { 2 | font-weight: 600; 3 | } 4 | 5 | .active { 6 | color: var(--color-active); 7 | } 8 | 9 | @media screen and (max-width: 768px) { 10 | .container a { 11 | font-size: 0.9rem; 12 | } 13 | } -------------------------------------------------------------------------------- /components/Storefront/Product/index.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from "react"; 2 | import { Col } from "react-bootstrap"; 3 | import styles from './styles.module.css'; 4 | import Game from '../../../dtos/Game'; 5 | 6 | type ProductProps = { 7 | product: Game; 8 | } & HTMLAttributes; 9 | 10 | const Product: React.FC = ({ product, ...rest }) => { 11 | return ( 12 | 16 |
17 | {product?.name} 22 |
23 | 24 |
25 |
26 | 27 | {product?.name} 28 | 29 | 30 | 31 | {product?.description} 32 | 33 |
34 |
35 | 36 | ); 37 | } 38 | 39 | export default Product; -------------------------------------------------------------------------------- /components/Storefront/Product/styles.module.css: -------------------------------------------------------------------------------- 1 | .product { 2 | cursor: pointer; 3 | padding: 0; 4 | margin-bottom: 10px; 5 | } 6 | 7 | .product span { 8 | display: block; 9 | overflow: hidden; 10 | white-space: nowrap; 11 | text-overflow: ellipsis; 12 | } 13 | 14 | .product img { 15 | height: 200px; 16 | } -------------------------------------------------------------------------------- /components/Storefront/ProductLicensesModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Modal, 3 | Button, 4 | Col, 5 | Row, 6 | InputGroup, 7 | FormControl 8 | } from 'react-bootstrap'; 9 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 10 | import { faKey } from '@fortawesome/free-solid-svg-icons'; 11 | import styles from './styles.module.css'; 12 | import Game from '../../../dtos/Game'; 13 | 14 | interface ProductLicensesModalProps { 15 | show: boolean; 16 | onHide: () => void; 17 | selectedProduct?: Game; 18 | } 19 | 20 | const ProductLicensesModal: React.FC = 21 | ({ show, onHide, selectedProduct }) => { 22 | return ( 23 | 30 | 31 | 32 | {selectedProduct?.name} 33 | 34 | 35 | 36 | 37 | 38 | Chaves de Ativação 39 | { 40 | selectedProduct?.licenses.map( 41 | (license, index) => 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 55 | 56 | ) 57 | } 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | ); 68 | } 69 | 70 | export default ProductLicensesModal; -------------------------------------------------------------------------------- /components/Storefront/ProductLicensesModal/styles.module.css: -------------------------------------------------------------------------------- 1 | .modal div { 2 | background-color: var(--color-primary) !important; 3 | border: none; 4 | } 5 | 6 | .modal span { 7 | color: white; 8 | } 9 | 10 | .modal strong { 11 | display: block; 12 | margin-bottom: 10px; 13 | } 14 | 15 | .key_input, 16 | .key_input:focus, 17 | .key_input:disabled { 18 | background-color: var(--color-secondary); 19 | border: none; 20 | border-radius: 5px; 21 | color: white; 22 | } -------------------------------------------------------------------------------- /components/Storefront/WishItem/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import BlueBackground from '../../../components/shared/BlueBackground'; 4 | 5 | import { Col, Row, Badge } from 'react-bootstrap'; 6 | 7 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 8 | import { faHeart, faCartPlus, faHeartBroken, IconDefinition } from '@fortawesome/free-solid-svg-icons'; 9 | 10 | import StyledButton from '../../../components/shared/StyledButton'; 11 | 12 | import styles from './styles.module.css'; 13 | 14 | import ProductSearch from '../../../dtos/ProductSearch'; 15 | 16 | interface WishItemProps { 17 | wishItem: ProductSearch; 18 | handleWishlistItemRemoval: Function; 19 | } 20 | 21 | const WishItem: React.FC = ({ wishItem, handleWishlistItemRemoval }) => { 22 | const [icon, setIcon] = useState(faHeart); 23 | 24 | return ( 25 |
26 | 27 | 28 | 29 | setIcon(faHeartBroken) 35 | } 36 | onMouseLeave={ 37 | () => setIcon(faHeart) 38 | } 39 | onClick={ 40 | () => handleWishlistItemRemoval(wishItem.id) 41 | } 42 | /> 43 | 44 | 45 | 46 | {wishItem.name} 51 | 52 | 53 | 54 |
{wishItem.name}
55 | 56 | { 57 | wishItem?.categories?.map( 58 | category => 59 | 63 | {category.name} 64 | 65 | ) 66 | } 67 | 68 | 69 | 70 | {`R$ ${wishItem.price}`} 71 | 72 |
73 | 78 |
79 | 80 |
81 |
82 |
83 | ); 84 | } 85 | 86 | export default WishItem; -------------------------------------------------------------------------------- /components/Storefront/WishItem/styles.module.css: -------------------------------------------------------------------------------- 1 | .action { 2 | text-align: right; 3 | } 4 | 5 | .product_image { 6 | width: 150px; 7 | height: 100px; 8 | border-radius: 5px; 9 | } 10 | 11 | .product_data h6 { 12 | font-weight: 600; 13 | } 14 | 15 | .primary_badge { 16 | background-color: var(--color-secondary); 17 | } 18 | 19 | .icon { 20 | cursor: pointer; 21 | transition: color 0.2s; 22 | } 23 | 24 | .icon:hover { 25 | color: #ff0a0a; 26 | } 27 | 28 | .icon:active { 29 | color: #ff5f5f; 30 | } 31 | 32 | @media screen and (max-width: 768px) { 33 | .action { 34 | text-align: center; 35 | margin-top: 10px; 36 | } 37 | 38 | .action strong { 39 | font-size: 1.2em; 40 | } 41 | 42 | .action button { 43 | width: 100%; 44 | margin-top: 10px; 45 | } 46 | 47 | .product_data { 48 | text-align: center; 49 | margin-top: 20px; 50 | } 51 | } -------------------------------------------------------------------------------- /components/shared/AdminComponent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminHeader from '../AdminHeader'; 3 | import AdminFooter from '../AdminFooter'; 4 | import { Col, Row } from 'react-bootstrap'; 5 | import LateralMenu from '../LateralMenu'; 6 | 7 | const AdminComponent: React.FC = ({children}) => { 8 | return( 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 |
19 | { children } 20 |
21 | 22 | 23 |
24 | 25 |
26 | ) 27 | } 28 | 29 | export default AdminComponent; -------------------------------------------------------------------------------- /components/shared/AdminDeleteModal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, Row, Col } from 'react-bootstrap'; 3 | import { faTrash, faTimes } from '@fortawesome/free-solid-svg-icons'; 4 | import styles from './styles.module.css'; 5 | import StyledButton from '../StyledButton'; 6 | 7 | interface AdminDeleteModalProps { 8 | show: boolean, 9 | handleClose: (success: boolean) => void, 10 | target: String 11 | } 12 | 13 | const AdminDeleteModal: React.FC = ({show, handleClose, target}) => { 14 | return ( 15 | 16 | 17 | Tem certeza que deseja excluir este {target}? 18 | 19 | 20 | 21 |
handleClose(true)}> 22 | 23 |
24 | 25 | 26 | 27 |
handleClose(false)}> 28 | 29 |
30 | 31 |
32 |
33 |
34 | ) 35 | } 36 | 37 | export default AdminDeleteModal; -------------------------------------------------------------------------------- /components/shared/AdminDeleteModal/styles.module.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | top: 20%; 3 | } 4 | 5 | .modal_body { 6 | background-color: var(--color-primary); 7 | padding: 50px; 8 | text-align: center; 9 | font-size: 29px; 10 | } -------------------------------------------------------------------------------- /components/shared/AdminFooter/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Col, Row, Container } from 'react-bootstrap'; 3 | import Logo from '../Logo'; 4 | 5 | const AdminFooter: React.FC = () => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | onebitcode.com • contato@onebitcode.com 15 | 16 | 17 | 18 | ) 19 | } 20 | 21 | export default AdminFooter; -------------------------------------------------------------------------------- /components/shared/AdminHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Row, Col } from 'react-bootstrap'; 3 | import styles from './styles.module.css'; 4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 5 | import { faSignal, faUser, faGamepad, faCheckSquare, faLaptop, faTicketAlt, faDollarSign, faUserCircle, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; 6 | import Link from 'next/link'; 7 | 8 | import { useRouter } from 'next/router'; 9 | 10 | import { useSelector } from 'react-redux'; 11 | import User from '../../../dtos/User'; 12 | import SignOutService from '../../../util/SignOutService'; 13 | 14 | const AdminHeader: React.FC = () => { 15 | const router = useRouter(); 16 | 17 | const { name }: User = useSelector(state => state.auth.loggedUser); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | SignOutService.execute()} 91 | > 92 | 96 | 97 | 98 | 99 | 100 | 101 | {name} 102 | 103 | 104 | 105 | 106 | 107 | ) 108 | } 109 | 110 | export default AdminHeader; -------------------------------------------------------------------------------- /components/shared/AdminHeader/styles.module.css: -------------------------------------------------------------------------------- 1 | .background { 2 | background-color: var(--color-primary); 3 | padding: 10px; 4 | border-radius: 10px; 5 | margin-top: 100px; 6 | } 7 | 8 | .name { 9 | color: var(--color-gray-light); 10 | font-size: 0.9rem; 11 | margin-right: 10px; 12 | } 13 | 14 | .background a svg:hover { 15 | cursor: pointer; 16 | color: var(--color-hover); 17 | transition: color 0.2s; 18 | } 19 | 20 | .active { 21 | color: var(--color-active); 22 | } 23 | 24 | .menu { 25 | display: flex; 26 | align-items: center; 27 | justify-content: left; 28 | } 29 | 30 | .profile { 31 | display: flex; 32 | align-items: center; 33 | justify-content: flex-end; 34 | margin-left: auto; 35 | margin-right: 0; 36 | } 37 | 38 | .profile span { 39 | transition: color 0.2s; 40 | } 41 | 42 | .profile span:hover { 43 | color: var(--color-hover); 44 | } 45 | 46 | .profile span:active { 47 | color: var(--color-active); 48 | } 49 | 50 | @media screen and (max-width: 990px) { 51 | .background { 52 | margin: 20px 1px; 53 | } 54 | 55 | .menu { 56 | justify-content: space-around; 57 | } 58 | 59 | .profile { 60 | margin: 0; 61 | } 62 | 63 | .profile span { 64 | display: none; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /components/shared/AdminListTable/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | import { Table } from 'react-bootstrap'; 4 | import Pagination from '../Pagination'; 5 | import Meta from '../../../dtos/Meta'; 6 | 7 | interface AdminListTableProps { 8 | first_title: string; 9 | second_title?: string; 10 | third_title?: string; 11 | fourth_title?: string; 12 | fifth_title?: string; 13 | sixth_title?: string; 14 | meta?: Meta; 15 | } 16 | 17 | const AdminListTable: React.FC = ({ children, first_title, second_title, third_title, fourth_title, fifth_title, sixth_title, meta }) => { 18 | return ( 19 |
20 | 21 | 22 | 23 | {first_title && } 24 | {second_title && } 25 | {third_title && } 26 | {fourth_title && } 27 | {fifth_title && } 28 | {sixth_title && } 29 | 30 | 31 | 32 | 33 | 34 | {children} 35 | 36 |
{first_title}{second_title}{third_title}{fourth_title}{fifth_title}{sixth_title}Ações
37 | 38 | 39 |
40 | ) 41 | } 42 | 43 | export default AdminListTable; -------------------------------------------------------------------------------- /components/shared/AdminListTable/styles.module.css: -------------------------------------------------------------------------------- 1 | .admin_panel { 2 | background-color: var(--color-secondary); 3 | padding: 30px; 4 | border-radius: 10px; 5 | margin-top: 20px; 6 | } 7 | 8 | .admin_panel table { 9 | color: white; 10 | border-collapse: separate; 11 | border-spacing: 0 1em; 12 | } 13 | 14 | .table_line { 15 | background-color: var(--color-background); 16 | padding-right: 110px; 17 | } 18 | 19 | .table_line:hover { 20 | color: white!important; 21 | } 22 | 23 | /* 24 | estamos determinando que iremos apenas aplicar estilo nos elementos que seguirem 25 | a ordem abaixo 26 | 27 | <* .admin_panel> 28 | 29 | 30 | 31 | -> aqui será aplicado o estilo 32 | 33 | 34 |
35 | 36 | 37 | o mesmo se aplica nos demais estilos abaixo 38 | */ 39 | .admin_panel table thead tr th { 40 | text-align: left; 41 | } 42 | 43 | .admin_panel table thead tr th:last-of-type { 44 | text-align: center; 45 | } 46 | 47 | .admin_panel table tbody tr { 48 | background-color: var(--color-background); 49 | padding-right: 110px; 50 | } 51 | 52 | .admin_panel table tbody tr:hover { 53 | color: white !important; 54 | } 55 | 56 | .admin_panel table tr td div:hover { 57 | cursor: pointer; 58 | color: var(--color-hover); 59 | transition: color 0.2s; 60 | } -------------------------------------------------------------------------------- /components/shared/Badge/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.css'; 2 | 3 | const Badge: React.FC = ({children}) => { 4 | return ( 5 |
6 | {children} 7 |
8 | ) 9 | } 10 | 11 | export default Badge; -------------------------------------------------------------------------------- /components/shared/Badge/styles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: inline-block; 3 | position: relative; 4 | top: -15px; 5 | right: 5px; 6 | } 7 | 8 | .container span { 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | height: 23px; 13 | width: 23px; 14 | border-radius: 50%; 15 | background: #f66a5d; 16 | font-size: 0.6em; 17 | font-weight: 900; 18 | padding-right: 1.5px; 19 | padding-left: 1.5px; 20 | user-select: none; 21 | } 22 | -------------------------------------------------------------------------------- /components/shared/BlueBackground/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | 4 | const BlueBackground: React.FC = ({ children }) => { 5 | return ( 6 |
7 | { children } 8 |
9 | ) 10 | } 11 | 12 | export default BlueBackground; -------------------------------------------------------------------------------- /components/shared/BlueBackground/styles.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | background-color: var(--color-primary); 3 | padding: 30px; 4 | border-radius: 10px; 5 | } -------------------------------------------------------------------------------- /components/shared/Footer/StorefrontFooter/styles.module.css: -------------------------------------------------------------------------------- 1 | .background { 2 | background-color: var(--color-primary); 3 | padding: 20px; 4 | font-size: 1rem; 5 | font-weight: 500; 6 | } 7 | 8 | @media screen and (max-width: 991px) { 9 | .logo { 10 | display: none !important; 11 | } 12 | 13 | .social_medias { 14 | display: flex; 15 | justify-content: space-around; 16 | text-align: center; 17 | } 18 | 19 | .social_medias svg { 20 | font-size: 1.4rem; 21 | } 22 | } -------------------------------------------------------------------------------- /components/shared/Header/StorefrontHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import styles from './styles.module.css'; 3 | import { InputGroup, FormControl, Row, Col } from 'react-bootstrap'; 4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 5 | import { faSearch, faShoppingCart, faUserCircle } from '@fortawesome/free-solid-svg-icons'; 6 | import Logo from '../../Logo'; 7 | 8 | import { useRouter } from 'next/router'; 9 | 10 | import LoggedService from '../../../../util/LoggedService'; 11 | 12 | import Badge from '../../Badge'; 13 | import CartModal from '../../../Storefront/CartModal'; 14 | 15 | import { useSelector } from 'react-redux'; 16 | import ProductShow from '../../../../dtos/ProductShow'; 17 | 18 | const CustomerHeader: React.FC = () => { 19 | const [search, setSearch] = useState(''); 20 | const [showCartModal, setShowCartModal] = useState(false); 21 | 22 | const cartProducts: ProductShow[] = useSelector(state => state.cartProducts); 23 | 24 | const router = useRouter(); 25 | 26 | const handleSearch = (): void => { 27 | router.push(` 28 | /Search?search=${search}&length=12&page=1&order=price&direction=asc 29 | `); 30 | } 31 | 32 | const handleUserRedirect = (): void => { 33 | router.push( 34 | LoggedService.execute() ? '/Profile' : 'Auth/Admin' 35 | ) 36 | } 37 | 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | ) => 55 | setSearch(evt.target.value) 56 | } 57 | 58 | onKeyUp={ 59 | (evt: React.KeyboardEvent) => { 60 | if (evt.key.toLowerCase() === 'enter') { 61 | handleSearch(); 62 | } 63 | } 64 | } 65 | /> 66 | 67 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 80 |
81 | setShowCartModal(!showCartModal)} 85 | /> 86 | { 87 | cartProducts?.length > 0 && 88 | {cartProducts.length} 89 | } 90 | 91 | { 92 | cartProducts?.length > 0 && showCartModal && 93 | 94 | } 95 |
96 | 97 | 98 | 99 | 104 | 105 |
106 | 107 |
108 | 109 |
110 | ) 111 | } 112 | 113 | export default CustomerHeader; -------------------------------------------------------------------------------- /components/shared/Header/StorefrontHeader/styles.module.css: -------------------------------------------------------------------------------- 1 | .background { 2 | background-color: var(--color-primary); 3 | padding: 20px; 4 | font-size: 1.4rem; 5 | font-weight: 500; 6 | } 7 | 8 | .background svg { 9 | cursor: pointer; 10 | transition: color 0.2s; 11 | } 12 | 13 | .background svg:hover { 14 | color: var(--color-hover); 15 | } 16 | 17 | .background svg:active { 18 | color: var(--color-active); 19 | } 20 | 21 | .hidden { 22 | display: none; 23 | } 24 | 25 | .cart_container { 26 | max-width: 72px; 27 | margin: 0 auto; 28 | } -------------------------------------------------------------------------------- /components/shared/LateralMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | import Logo from '../Logo'; 4 | import Link from 'next/link'; 5 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 6 | import { faSignal, faUser, faGamepad, faCheckSquare, faLaptop, faTicketAlt, faDollarSign, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; 7 | 8 | import { useRouter } from 'next/router'; 9 | 10 | import SignOutService from '../../../util/SignOutService'; 11 | 12 | const LateralMenu: React.FC = () => { 13 | const router = useRouter(); 14 | 15 | return ( 16 |
17 | 18 | 19 | 88 |
89 | ) 90 | } 91 | 92 | export default LateralMenu; -------------------------------------------------------------------------------- /components/shared/LateralMenu/styles.module.css: -------------------------------------------------------------------------------- 1 | .background { 2 | background-color: var(--color-primary); 3 | padding: 40px; 4 | height: 100%; 5 | } 6 | 7 | .list { 8 | margin-top: 50px; 9 | } 10 | 11 | .active { 12 | color: var(--color-active); 13 | } 14 | 15 | .active svg { 16 | color: var(--color-active); 17 | } 18 | 19 | @media screen and (max-width: 990px) { 20 | .background { 21 | visibility: hidden; 22 | height: 0; 23 | } 24 | } -------------------------------------------------------------------------------- /components/shared/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | import Image from 'next/image'; 4 | import styles from './styles.module.css'; 5 | 6 | const Logo: React.FC = () => { 7 | return ( 8 | 9 | 10 | Logo OneBitGames 11 | 12 | 13 | ) 14 | } 15 | 16 | export default Logo; -------------------------------------------------------------------------------- /components/shared/Logo/styles.module.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 990px) { 2 | .logo { 3 | display: block; 4 | text-align: center; 5 | } 6 | } -------------------------------------------------------------------------------- /components/shared/MainComponent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../Header/StorefrontHeader'; 3 | import Footer from '../Footer/StorefrontFooter'; 4 | 5 | const MainComponent: React.FC = ({children}) => { 6 | return ( 7 |
8 |
9 | 10 |
11 | {children} 12 |
13 | 14 |
15 |
16 | ) 17 | } 18 | 19 | export default MainComponent; -------------------------------------------------------------------------------- /components/shared/NoData/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.css'; 2 | 3 | interface NoDataProps { 4 | message?: string 5 | } 6 | 7 | const NoData: React.FC = 8 | ({ message = 'Não há dados cadastrados ou encontrados =(' }) => { 9 | return ( 10 |
11 | { message } 12 |
13 | ) 14 | } 15 | 16 | export default NoData; -------------------------------------------------------------------------------- /components/shared/NoData/styles.module.css: -------------------------------------------------------------------------------- 1 | .admin_panel { 2 | background-color: var(--color-secondary); 3 | padding: 30px; 4 | border-radius: 10px; 5 | margin-top: 20px; 6 | } -------------------------------------------------------------------------------- /components/shared/OrderDetail/index.tsx: -------------------------------------------------------------------------------- 1 | import { Row, Col, Badge } from "react-bootstrap"; 2 | import BlueBackground from "../BlueBackground"; 3 | 4 | import styles from './styles.module.css'; 5 | 6 | import OrderItem from '../../../dtos/OrderItem'; 7 | 8 | interface OrderDetailProps { 9 | items: OrderItem[]; 10 | } 11 | 12 | const OrderDetail: React.FC = ({items}) => { 13 | return ( 14 | 15 | PRODUTO 16 | VALOR 17 | 18 | { 19 | items?.map( 20 | (item, index) => 21 |
22 |
23 | 24 | 25 | 30 | {item?.product} 34 | 35 | 36 | 37 | 38 | {item?.product} 39 | 40 | 41 |
42 | { 43 | item?.categories?.map( 44 | (category, index) => 45 | 50 | {category} 51 | 52 | ) 53 | } 54 |
55 | 56 | 57 | 58 | 59 | 60 | {item?.payed_price} 61 | 62 | 63 |
64 |
65 | ) 66 | } 67 |
68 | ) 69 | } 70 | 71 | export default OrderDetail; -------------------------------------------------------------------------------- /components/shared/OrderDetail/styles.module.css: -------------------------------------------------------------------------------- 1 | .line { 2 | background-color: white; 3 | opacity: 0.2; 4 | } 5 | 6 | .product img { 7 | width: 150px; 8 | height: 100px; 9 | margin-bottom: 20px; 10 | } 11 | 12 | .primary_badge { 13 | opacity: 0.6; 14 | background-color: var(--color-secondary); 15 | } 16 | 17 | @media screen and (max-width: 767px) { 18 | .product_name { 19 | display: inline-block; 20 | font-size: 0.9em; 21 | max-width: 158px; 22 | overflow: hidden; 23 | white-space: nowrap; 24 | text-overflow: ellipsis; 25 | } 26 | 27 | .price { 28 | font-size: 0.9em; 29 | } 30 | } -------------------------------------------------------------------------------- /components/shared/OrdersList/index.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from 'react-bootstrap'; 2 | import styles from './styles.module.css'; 3 | import OrderStatusService from '../../../util/OrderStatusService'; 4 | 5 | import Link from 'next/link'; 6 | import OrdersListDTO from '../../../dtos/OrdersList'; 7 | 8 | interface OrdersListProps { 9 | orders: OrdersListDTO[]; 10 | admin?: boolean; 11 | } 12 | 13 | const OrdersList: React.FC = ({orders, admin=false}) => { 14 | return ( 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | { 32 | orders?.map( 33 | order => 34 | 38 | 45 | 51 | 52 | 53 | 54 | ) 55 | } 56 | 57 |
NúmeroPagamentoValorSituação
39 | 40 | 41 | {`#${order?.id}`} 42 | 43 | 44 | 46 | { 47 | order?.payment_type === 'billet' ? 48 | 'Boleto' : 'Cartão de Crédito' 49 | } 50 | {`R$ ${order?.total_amount}`}{OrderStatusService.execute(order?.status)}
58 | ) 59 | } 60 | 61 | export default OrdersList; -------------------------------------------------------------------------------- /components/shared/OrdersList/styles.module.css: -------------------------------------------------------------------------------- 1 | .table { 2 | border-collapse: separate; 3 | border-spacing: 0 1em; 4 | } 5 | 6 | .table_line { 7 | background-color: var(--color-background); 8 | padding-right: 110px; 9 | } 10 | 11 | .table tr, 12 | .table th, 13 | .table td { 14 | color: white; 15 | } 16 | 17 | .table td { 18 | width: 20%; 19 | } 20 | 21 | .secondary_color { 22 | color: var(--color-gray-light); 23 | } -------------------------------------------------------------------------------- /components/shared/Pagination/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, HtmlHTMLAttributes } from 'react'; 2 | import Meta from '../../../dtos/Meta'; 3 | import StyledButton from '../StyledButton'; 4 | import PaginationService from '../../../util/PaginationService'; 5 | 6 | import { useRouter } from 'next/router'; 7 | 8 | type PaginationProps = HtmlHTMLAttributes & { 9 | page: number; 10 | total_pages: number; 11 | } 12 | 13 | const Pagination: React.FunctionComponent = 14 | ({ page, total_pages, ...rest }: PaginationProps) => { 15 | 16 | const [pagination, setPagination] = useState(['1']); 17 | const router = useRouter(); 18 | 19 | //sempre que o total_pages mudar, o array de listagem também deverá mudar 20 | useEffect(() => { 21 | setPagination(PaginationService.execute(total_pages, page)); 22 | }, [total_pages]) 23 | 24 | // método utilizado para tratar a seleção das páginas 25 | const handlePageClick = (page: string): void => { 26 | router.push({ 27 | pathname: router.pathname, 28 | query: { 29 | ...router.query, 30 | page 31 | } 32 | }) 33 | } 34 | 35 | // método para tratar a seleção da página posterior a atual 36 | const handleNextPageClick = (): void => { 37 | if (page < total_pages) { 38 | router.push({ 39 | pathname: router.pathname, 40 | query: { 41 | ...router.query, 42 | page: page + 1 43 | } 44 | }) 45 | } 46 | } 47 | 48 | // método para tratar a seleção da página anterior a atual 49 | const handlePreviusPageClick = (): void => { 50 | if (page > 1) { 51 | router.push({ 52 | pathname: router.pathname, 53 | query: { 54 | ...router.query, 55 | page: page - 1 56 | } 57 | }) 58 | } 59 | } 60 | 61 | return ( 62 |
63 |
64 | 69 | 70 | { 71 | // quando o item do array da páginação for '...' será apenas renderizado o texto '...', caso contrário será renderizado um botão com os números das páginas 72 | pagination.map((item, index) => ( 73 | item === '...' ? '...' : ( 74 | handlePageClick(item)} 80 | /> 81 | ) 82 | )) 83 | } 84 | 85 | 90 |
91 |
92 | ) 93 | } 94 | 95 | export default Pagination; -------------------------------------------------------------------------------- /components/shared/PasswordComponent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import BlueBackground from '../BlueBackground'; 3 | import { Row, Col } from 'react-bootstrap'; 4 | 5 | const PasswordComponent: React.FC = ({children}) => { 6 | return ( 7 | 8 | 9 | 10 |

Recuperar senha

11 | 12 | {children} 13 |
14 | 15 |
16 | ) 17 | } 18 | 19 | export default PasswordComponent; -------------------------------------------------------------------------------- /components/shared/ProductInfo/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'react-bootstrap'; 2 | import styles from './styles.module.css'; 3 | import ProductHome from '../../../dtos/ProductHome'; 4 | 5 | import { useRouter } from 'next/router'; 6 | 7 | interface ProductInfoProps { 8 | type?: string; 9 | product: ProductHome; 10 | } 11 | 12 | const ProductInfo: React.FC = ({ type = 'normal', product }) => { 13 | const router = useRouter(); 14 | 15 | return ( 16 |
17 |
18 | {product.name} 23 |
24 | 25 |
26 |
27 |

28 | {product.name} 29 |

30 |

31 | {product.description} 32 |

33 |
34 | 35 |
36 | 44 |
45 |
46 |
47 | ) 48 | } 49 | 50 | export default ProductInfo; -------------------------------------------------------------------------------- /components/shared/ProductInfo/styles.module.css: -------------------------------------------------------------------------------- 1 | .product { 2 | margin: 15px auto; 3 | } 4 | 5 | .image { 6 | width: 100%; 7 | max-height: 143.44px; 8 | } 9 | 10 | .button button { 11 | margin-top: 20px; 12 | width: 100%; 13 | border-radius: 2px; 14 | } 15 | 16 | .product_details { 17 | background-color: var(--color-primary); 18 | padding: 10px; 19 | } 20 | 21 | .product_details p { 22 | margin: 0; 23 | height: 25px; 24 | overflow: hidden; 25 | white-space: nowrap; 26 | text-overflow: ellipsis; 27 | } 28 | 29 | .normal_button { 30 | background: var(--color-secondary); 31 | border: none; 32 | } 33 | 34 | @media screen and (max-width: 1199px) { 35 | .image { 36 | max-height: 118.13px; 37 | } 38 | } 39 | 40 | @media screen and (max-width: 991px) { 41 | .image { 42 | max-height: 84.38px; 43 | } 44 | 45 | .product_details p { 46 | font-size: 0.9em; 47 | } 48 | } 49 | 50 | @media screen and (max-width: 767px) { 51 | .image { 52 | max-height: 250px; 53 | } 54 | } -------------------------------------------------------------------------------- /components/shared/StyledButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | import { Button } from 'react-bootstrap'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 5 | import styles from './styles.module.css'; 6 | 7 | type ButtonProps = ButtonHTMLAttributes & { 8 | icon?: IconProp; 9 | action?: string; 10 | type_button: string; 11 | active?: boolean; 12 | } 13 | 14 | const StyledButton: React.FC = ({icon, action, type_button, active = false, ...rest}) => { 15 | return ( 16 | 26 | ) 27 | } 28 | 29 | export default StyledButton; -------------------------------------------------------------------------------- /components/shared/StyledButton/styles.module.css: -------------------------------------------------------------------------------- 1 | .red_button { 2 | background: none; 3 | border: 2px solid rgb(200,10,10); 4 | } 5 | 6 | .red_button:hover { 7 | background: none; 8 | } 9 | 10 | .blue_button { 11 | background: none; 12 | border: 2px solid rgb(10,10,200); 13 | } 14 | 15 | .blue_button:hover { 16 | background: none; 17 | border: 2px solid rgb(10,200,200); 18 | } -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/SearchAndIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 3 | import styles from './styles.module.css'; 4 | import { InputGroup, FormControl, Row, Col } from 'react-bootstrap'; 5 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 6 | import { faSearch } from '@fortawesome/free-solid-svg-icons'; 7 | import StyledButton from '../../StyledButton'; 8 | import Link from 'next/link'; 9 | 10 | import { useDispatch } from 'react-redux'; 11 | import { 12 | setSearch as setSearchRedux, 13 | clearSearch 14 | } from '../../../../store/modules/admin/shared/search/reducer'; 15 | 16 | import { useRouter } from 'next/router'; 17 | 18 | interface SearchAndIcon { 19 | icon: IconProp; 20 | newPath: string; 21 | } 22 | 23 | const SearchAndIcon: React.FC = ({icon, newPath}) => { 24 | const [search, setSearch] = useState(''); 25 | const [placeholder, setPlaceholder] = useState(''); 26 | const dispatch = useDispatch(); 27 | const router = useRouter(); 28 | 29 | useEffect(() => { 30 | dispatch(clearSearch()); 31 | }, []) 32 | 33 | useEffect(() => { 34 | switch(router.pathname) { 35 | case '/Admin/Products/List': 36 | setPlaceholder('Pesquisar produto'); 37 | break; 38 | case '/Admin/Categories/List': 39 | setPlaceholder('Pesquisar categoria'); 40 | break; 41 | case '/Admin/SystemRequirements/List': 42 | setPlaceholder('Pesquisar requisitos de sistema'); 43 | break; 44 | case '/Admin/Coupons/List': 45 | setPlaceholder('Pesquisar cupons'); 46 | break; 47 | default: 48 | setPlaceholder('Pesquisar usuário'); 49 | break; 50 | } 51 | }, [router.pathname]) 52 | 53 | const handleSearch = (): void => { 54 | router.replace(router.pathname, '?page=1'); 55 | dispatch(setSearchRedux(search)); 56 | } 57 | 58 | return ( 59 | 60 | 61 | 62 | 63 | 64 | ) => 70 | setSearch(evt.target.value) 71 | } 72 | 73 | onKeyPress={ 74 | (evt: React.KeyboardEvent) => { 75 | if (evt.key.toLocaleLowerCase() === 'enter') { 76 | handleSearch(); 77 | } 78 | } 79 | } 80 | /> 81 | 82 | 83 | 84 | 85 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | ) 105 | } 106 | 107 | export default SearchAndIcon; -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/SearchAndIcon/styles.module.css: -------------------------------------------------------------------------------- 1 | .input { 2 | background-color: var(--color-secondary); 3 | border: none; 4 | height: 30px; 5 | } 6 | 7 | .input:focus { 8 | background-color: var(--color-secondary); 9 | } 10 | 11 | .titleButton { 12 | margin-top: -5px; 13 | } 14 | 15 | .search_icon svg { 16 | margin-top: 3.5px; 17 | cursor: pointer; 18 | transition: color 0.2s; 19 | } 20 | 21 | .search_icon svg:hover { 22 | color: var(--color-hover); 23 | } -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/TitleAndPath/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | 4 | interface TitleAndPath { 5 | title: String, 6 | path: String 7 | } 8 | 9 | const TitleAndPath: React.FC = ({title, path}) => { 10 | return ( 11 | <> 12 |

{ title }

13 | { path } 14 | 15 | ) 16 | } 17 | 18 | export default TitleAndPath; -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/TitleAndPath/styles.module.css: -------------------------------------------------------------------------------- 1 | .styledPath { 2 | color: var(--color-gray-light); 3 | font-size: 11px; 4 | margin-left: 10px; 5 | margin-top: 10px; 6 | } 7 | 8 | @media (max-width: 1200px) { 9 | .styledPath { 10 | display: none!important; 11 | } 12 | 13 | .title { 14 | margin-left: 10px; 15 | } 16 | } -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Row, Col } from 'react-bootstrap'; 3 | import styles from './styles.module.css'; 4 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 5 | import TitleAndPath from './TitleAndPath'; 6 | import SearchAndIcon from './SearchAndIcon'; 7 | 8 | interface TitleAdminPanelProps { 9 | title: string; 10 | path: string; 11 | icon?: IconProp; 12 | newPath?: string; 13 | } 14 | 15 | const TitleAdminPanel: React.FC = ({ title, path, icon, newPath = '#' }) => { 16 | return ( 17 | 18 | { 19 | (icon) ? 20 | <> 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | : 30 | 31 | } 32 | 33 | ) 34 | } 35 | 36 | export default TitleAdminPanel; -------------------------------------------------------------------------------- /components/shared/TitleAdminPanel/styles.module.css: -------------------------------------------------------------------------------- 1 | .title_and_path { 2 | text-align: left; 3 | } 4 | 5 | .title_and_path h4, .title_and_path span { 6 | display: inline; 7 | } 8 | -------------------------------------------------------------------------------- /components/withAuth/index.tsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | import { useRouter } from 'next/router'; 3 | import Cookie from 'js-cookie'; 4 | 5 | import AuthState from '../../dtos/AuthState'; 6 | import User from '../../dtos/User'; 7 | import ApiData from '../../dtos/ApiData'; 8 | 9 | const withAuth = (Component) => { 10 | const Auth = (props) => { 11 | const router = useRouter(); 12 | const loggedUser: User = useSelector((state: AuthState) => state.auth.loggedUser); 13 | 14 | const apiDataCookie = Cookie.get('@api-data'); 15 | const apiData: ApiData = apiDataCookie ? JSON.parse(apiDataCookie) : null; 16 | 17 | if (!loggedUser || 18 | !apiData || 19 | !apiData['access-token'] || 20 | apiData['access-token'] === '' 21 | ) { 22 | router.push({ 23 | pathname: '/Auth/Login', 24 | query: { 25 | callback: router.pathname 26 | } 27 | }); 28 | } 29 | 30 | return ; 31 | } 32 | 33 | if (Component.getServerSideProps) { 34 | Auth.getServerSideProps = Component.getServerSideProps; 35 | } 36 | 37 | return Auth; 38 | } 39 | 40 | export default withAuth; -------------------------------------------------------------------------------- /components/withAuthAdmin/index.tsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | import { useRouter } from 'next/router'; 3 | import Cookie from 'js-cookie'; 4 | 5 | import AuthState from '../../dtos/AuthState'; 6 | import User from '../../dtos/User'; 7 | import ApiData from '../../dtos/ApiData'; 8 | 9 | const withAuthAdmin = (Component) => { 10 | const Auth = (props) => { 11 | const router = useRouter(); 12 | const loggedUser: User = useSelector((state: AuthState) => state.auth.loggedUser); 13 | 14 | const apiDataCookie = Cookie.get('@api-data'); 15 | const apiData: ApiData = apiDataCookie ? JSON.parse(apiDataCookie) : null; 16 | 17 | // checando se o usuário existe no redux e se o mesmo é admin 18 | // checando se os dados da api existem no cookie e ainda se existe 19 | // o access-token salvo. 20 | if (!loggedUser || 21 | loggedUser.profile !== 'admin' || 22 | !apiData || 23 | !apiData['access-token'] || 24 | apiData['aceess-token'] === '' 25 | ) { 26 | router.push({ 27 | pathname: '/Auth/Login', 28 | query: { 29 | callback: router.pathname 30 | } 31 | }) 32 | } 33 | 34 | return ; 35 | } 36 | 37 | // se o component tiver o método getServerSideProps (responsável por 38 | // fazer o fetch das props e realizar o pre-render da página no server side) 39 | // ele irá repassar para o component auth, para que assim as props sejam 40 | // acessiveis pelo Auth e caso o usuário tenha acesso a página, essas props 41 | // serão repassadas ao component (linha 19) 42 | if(Component.getServerSideProps) { 43 | Auth.getServerSideProps = Component.getServerSideProps; 44 | } 45 | 46 | return Auth; 47 | } 48 | 49 | export default withAuthAdmin; -------------------------------------------------------------------------------- /dtos/ApiData.ts: -------------------------------------------------------------------------------- 1 | export default interface ApiData { 2 | 'access-token': string; 3 | client: string; 4 | expiry: number; 5 | 'token-type': string; 6 | uid: string; 7 | } -------------------------------------------------------------------------------- /dtos/ApiResponseError.ts: -------------------------------------------------------------------------------- 1 | export default interface ApiResponseError { 2 | fields: { 3 | [key: string]: string[] 4 | } 5 | } -------------------------------------------------------------------------------- /dtos/AuthState.ts: -------------------------------------------------------------------------------- 1 | import User from './User'; 2 | 3 | export default interface AuthState { 4 | auth: { 5 | loggedUser: User; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /dtos/Category.ts: -------------------------------------------------------------------------------- 1 | export default interface Category { 2 | id: number; 3 | name: string; 4 | } -------------------------------------------------------------------------------- /dtos/Checkout.ts: -------------------------------------------------------------------------------- 1 | import CheckoutItem from './CheckoutItem'; 2 | 3 | interface Address { 4 | street: string; 5 | number: string; 6 | city: string; 7 | state: string; 8 | post_code: string; 9 | } 10 | 11 | interface Checkout { 12 | payment_type: string; 13 | installments: string; 14 | coupon_id?: number; 15 | card_hash?: string; 16 | document: string; 17 | items: CheckoutItem[]; 18 | address?: Address; 19 | } 20 | 21 | export default Checkout; -------------------------------------------------------------------------------- /dtos/CheckoutItem.ts: -------------------------------------------------------------------------------- 1 | export default interface CheckoutItem { 2 | product_id: number; 3 | quantity: number; 4 | } -------------------------------------------------------------------------------- /dtos/Coupon.ts: -------------------------------------------------------------------------------- 1 | export default interface Coupon { 2 | id: number; 3 | name: string; 4 | code: string; 5 | status: string; 6 | discount_value: number; 7 | due_date: string; 8 | } -------------------------------------------------------------------------------- /dtos/Dashboard.ts: -------------------------------------------------------------------------------- 1 | export default interface Dashboard { 2 | min_date: string; 3 | max_date: string; 4 | } -------------------------------------------------------------------------------- /dtos/Game.ts: -------------------------------------------------------------------------------- 1 | import Product from './Product'; 2 | 3 | type Game = { 4 | licenses: string[]; 5 | } & Pick; 6 | 7 | export default Game; -------------------------------------------------------------------------------- /dtos/HomeIndexData.ts: -------------------------------------------------------------------------------- 1 | import ProductHome from './ProductHome'; 2 | 3 | export default interface HomeIndexData { 4 | featured: ProductHome[]; 5 | 'last_releases': ProductHome[]; 6 | cheapest: ProductHome[]; 7 | }; -------------------------------------------------------------------------------- /dtos/Meta.ts: -------------------------------------------------------------------------------- 1 | export default interface Meta { 2 | page: number; 3 | length: number; 4 | total: number; 5 | total_pages: number; 6 | } -------------------------------------------------------------------------------- /dtos/Order.ts: -------------------------------------------------------------------------------- 1 | import OrderItem from './OrderItem'; 2 | 3 | export default interface Order { 4 | id: number; 5 | payment_type: string; 6 | installments: number; 7 | subtotal: number; 8 | discount: number; 9 | line_items: OrderItem[] 10 | } -------------------------------------------------------------------------------- /dtos/OrderItem.ts: -------------------------------------------------------------------------------- 1 | export default interface OrderItem { 2 | quantity: number; 3 | payed_price: number; 4 | product: string; 5 | categories: string[]; 6 | image_url: string; 7 | } 8 | -------------------------------------------------------------------------------- /dtos/OrdersList.ts: -------------------------------------------------------------------------------- 1 | import Order from './Order'; 2 | 3 | type OrdersList = { 4 | status: string; 5 | total_amount: string; 6 | } & Pick; 7 | 8 | export default OrdersList; -------------------------------------------------------------------------------- /dtos/Product.ts: -------------------------------------------------------------------------------- 1 | import Category from './Category'; 2 | import SystemRequirement from './SystemRequirement'; 3 | 4 | export default interface Product { 5 | id: number; 6 | name: string; 7 | description: string; 8 | price: number; 9 | status: string; 10 | image_url: string; 11 | productable: string; 12 | mode: string; 13 | developer: string; 14 | release_date: string; 15 | featured: string; 16 | categories: Category[]; 17 | system_requirement: SystemRequirement; 18 | } -------------------------------------------------------------------------------- /dtos/ProductHome.ts: -------------------------------------------------------------------------------- 1 | import Product from './Product'; 2 | 3 | type ProductHome = 4 | Pick; 5 | 6 | export default ProductHome; -------------------------------------------------------------------------------- /dtos/ProductSearch.ts: -------------------------------------------------------------------------------- 1 | import Product from './Product'; 2 | 3 | type ProductSearch = 4 | Pick; 5 | 6 | export default ProductSearch; -------------------------------------------------------------------------------- /dtos/ProductShow.ts: -------------------------------------------------------------------------------- 1 | import Product from './Product'; 2 | 3 | type ProductShow = { 4 | sells_count: number; 5 | favorited_count: number; 6 | } & Product; 7 | 8 | export default ProductShow; -------------------------------------------------------------------------------- /dtos/ProductShowData.ts: -------------------------------------------------------------------------------- 1 | import ProductShow from './ProductShow'; 2 | 3 | export default interface ProductShowData { 4 | product: ProductShow 5 | }; -------------------------------------------------------------------------------- /dtos/SystemRequirement.ts: -------------------------------------------------------------------------------- 1 | export default interface SystemRequirement { 2 | id: number; 3 | name: string; 4 | operational_system: string; 5 | storage: string; 6 | processor: string; 7 | memory: string; 8 | video_board: string; 9 | } -------------------------------------------------------------------------------- /dtos/User.ts: -------------------------------------------------------------------------------- 1 | export default interface User { 2 | id: number, 3 | name: string; 4 | email: string; 5 | profile: string; 6 | password?: string; 7 | password_confirmation?: string; 8 | } -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootcamp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev -p 3001", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-svg-core": "^1.2.32", 12 | "@fortawesome/free-brands-svg-icons": "^5.15.1", 13 | "@fortawesome/free-solid-svg-icons": "^5.15.1", 14 | "@fortawesome/react-fontawesome": "^0.1.12", 15 | "@reduxjs/toolkit": "^1.4.0", 16 | "axios": "^0.21.0", 17 | "bootstrap": "^4.5.3", 18 | "date-fns": "^2.19.0", 19 | "js-cookie": "^2.2.1", 20 | "next": "10.0.1", 21 | "react": "17.0.1", 22 | "react-bootstrap": "^1.4.0", 23 | "react-dom": "17.0.1", 24 | "react-redux": "^7.2.2", 25 | "react-text-mask": "^5.4.3", 26 | "react-toastify": "^6.2.0", 27 | "recharts": "^2.1.2", 28 | "redux": "^4.0.5", 29 | "redux-persist": "^6.0.0", 30 | "swr": "^0.3.8" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^14.14.7", 34 | "@types/react": "^16.9.56", 35 | "typescript": "^4.0.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pages/Admin/Categories/Edit/index.tsx: -------------------------------------------------------------------------------- 1 | import AdminComponent from '../../../../components/shared/AdminComponent'; 2 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 3 | import Category from '../../../../dtos/Category'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import { useDispatch } from 'react-redux'; 8 | import { clearCategoryToEdit } from '../../../../store/modules/admin/category/reducer'; 9 | 10 | import CategoriesService from '../../../../services/categories'; 11 | import { toast } from 'react-toastify'; 12 | import { useRouter } from 'next/router'; 13 | 14 | import CategoryForm from '../../../../components/Admin/CategoryForm'; 15 | 16 | const Edit: React.FC = () => { 17 | const dispatch = useDispatch(); 18 | const router = useRouter(); 19 | 20 | const handleSubmit = async (category: Category): Promise => { 21 | try { 22 | await CategoriesService.update(category); 23 | 24 | toast.info('Categoria atualizada com sucesso!'); 25 | 26 | dispatch(clearCategoryToEdit()); 27 | router.back(); 28 | } catch (err) { 29 | toast.error('Ocorreu um erro ao atualizar a categoria, tente novamente.'); 30 | console.log(err); 31 | } 32 | } 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | export default withAuthAdmin(Edit); -------------------------------------------------------------------------------- /pages/Admin/Categories/New/index.tsx: -------------------------------------------------------------------------------- 1 | import AdminComponent from '../../../../components/shared/AdminComponent'; 2 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 3 | 4 | import CategoryForm from '../../../../components/Admin/CategoryForm'; 5 | 6 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 7 | 8 | import CategoriesService from '../../../../services/categories'; 9 | import { toast } from 'react-toastify'; 10 | import { useRouter } from 'next/router'; 11 | 12 | import Category from '../../../../dtos/Category'; 13 | 14 | const New: React.FC = () => { 15 | const router = useRouter(); 16 | 17 | const handleSubmit = async ({ name }: Category): Promise => { 18 | try { 19 | await CategoriesService.create(name); 20 | toast.info('Categoria cadastrada com sucesso!'); 21 | 22 | router.back(); 23 | } catch (err) { 24 | toast.error('Ocorreu algum erro, tente novamente!'); 25 | console.log(err); 26 | } 27 | } 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/Coupons/Edit/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import CouponForm from '../../../../components/Admin/CouponForm'; 8 | import Coupon from '../../../../dtos/Coupon'; 9 | 10 | import { useRouter } from 'next/router'; 11 | import { useDispatch } from 'react-redux'; 12 | 13 | import { clearCouponToEdit } from '../../../../store/modules/admin/coupon/reducer'; 14 | import { toast } from 'react-toastify'; 15 | 16 | import CouponsService from '../../../../services/coupons'; 17 | 18 | const Edit: React.FC = () => { 19 | const router = useRouter(); 20 | const dispatch = useDispatch(); 21 | 22 | const handleSubmit = async(coupon: Coupon): Promise => { 23 | try { 24 | await CouponsService.update(coupon); 25 | toast.info('Cupom atualizado com sucesso!'); 26 | dispatch(clearCouponToEdit()); 27 | router.back(); 28 | } catch (err) { 29 | toast.error('Erro ao atualizar o cupom, tente novamente.'); 30 | console.log(err); 31 | } 32 | } 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | export default withAuthAdmin(Edit); -------------------------------------------------------------------------------- /pages/Admin/Coupons/New/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import CouponForm from '../../../../components/Admin/CouponForm'; 8 | import Coupon from '../../../../dtos/Coupon'; 9 | import { toast } from 'react-toastify'; 10 | 11 | import { useRouter } from 'next/router'; 12 | import CouponsService from '../../../../services/coupons'; 13 | 14 | const New: React.FC = () => { 15 | const router = useRouter(); 16 | 17 | const handleSubmit = async (coupon: Coupon): Promise => { 18 | try { 19 | await CouponsService.create(coupon); 20 | toast.info('Cupom salvo com sucesso!'); 21 | router.back(); 22 | } catch (err) { 23 | toast.error('Erro ao salvar o cupom, tente novamente.'); 24 | console.log(err); 25 | } 26 | } 27 | 28 | return ( 29 | 30 | 31 | 32 | 33 | 34 | ) 35 | } 36 | 37 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/Orders/List/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import AdminComponent from "../../../../components/shared/AdminComponent"; 3 | import TitleAdminPanel from "../../../../components/shared/TitleAdminPanel"; 4 | 5 | import styles from './styles.module.css'; 6 | 7 | import OrdersList from "../../../../components/shared/OrdersList"; 8 | import AdminOrdersService from "../../../../services/adminOrders"; 9 | import Pagination from "../../../../components/shared/Pagination"; 10 | 11 | import useSwr from 'swr'; 12 | import { toast } from "react-toastify"; 13 | import { useRouter } from 'next/router'; 14 | import UrlService from "../../../../util/UrlService"; 15 | 16 | import withAuthAdmin from "../../../../components/withAuthAdmin"; 17 | 18 | const defaultUrl = '/admin/v1/orders'; 19 | 20 | const Orders: React.FC = () => { 21 | const[url, setUrl] = useState(defaultUrl); 22 | const router = useRouter(); 23 | const { data, error } = useSwr(url, AdminOrdersService.index); 24 | 25 | useEffect(() => { 26 | setUrl( 27 | defaultUrl + 28 | UrlService.execute({ page: router?.query?.page }) 29 | ); 30 | }, [router?.query?.page]) 31 | 32 | if (error) { 33 | toast.error('Erro ao obter os dados dos pedidos!'); 34 | console.log(error); 35 | } 36 | 37 | return ( 38 | 39 | 40 | 41 |
42 | 46 | 47 | 48 |
49 |
50 | ); 51 | } 52 | 53 | export default withAuthAdmin(Orders); -------------------------------------------------------------------------------- /pages/Admin/Orders/List/styles.module.css: -------------------------------------------------------------------------------- 1 | .admin_panel { 2 | background-color: var(--color-secondary); 3 | padding: 30px; 4 | border-radius: 10px; 5 | margin-top: 20px; 6 | } -------------------------------------------------------------------------------- /pages/Admin/Orders/[id].tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from "../../../components/shared/MainComponent"; 2 | import StyledButton from "../../../components/shared/StyledButton"; 3 | 4 | import styles from './styles.module.css'; 5 | 6 | import { faArrowLeft } from "@fortawesome/free-solid-svg-icons"; 7 | 8 | import { useRouter } from 'next/router'; 9 | 10 | import withAuthAdmin from "../../../components/withAuthAdmin"; 11 | import OrderDetail from "../../../components/shared/OrderDetail"; 12 | 13 | import OrderService from "../../../services/order"; 14 | import useSwr from 'swr'; 15 | import { toast } from "react-toastify"; 16 | 17 | const Order: React.FC = () => { 18 | const router = useRouter(); 19 | const { id } = router?.query; 20 | 21 | const { data, error } = useSwr( 22 | () => id ? `/admin/v1/orders/${id}` : null, OrderService.show 23 | ); 24 | 25 | if (error) { 26 | toast.error('Erro ao obter os dados do pedido!'); 27 | console.log(error); 28 | } 29 | 30 | return ( 31 | 32 |
33 | Pedido 34 | {` #${data?.id}`} 35 | 36 |
37 | router.back()} 42 | type="button" 43 | /> 44 |
45 |
46 | 47 | 48 |
49 | ); 50 | } 51 | 52 | export default withAuthAdmin(Order); -------------------------------------------------------------------------------- /pages/Admin/Orders/styles.module.css: -------------------------------------------------------------------------------- 1 | .secondary_color { 2 | color: var(--color-gray-light); 3 | } 4 | 5 | .button_blue_light { 6 | color: var(--color-secondary); 7 | background-color: var(--color-blue-light); 8 | } 9 | 10 | .blue_text { 11 | color: var(--color-blue-light); 12 | } -------------------------------------------------------------------------------- /pages/Admin/Products/Edit/index.tsx: -------------------------------------------------------------------------------- 1 | import AdminComponent from '../../../../components/shared/AdminComponent'; 2 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 3 | 4 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 5 | 6 | import ProductForm from '../../../../components/Admin/ProductForm'; 7 | import ProductsService from '../../../../services/products'; 8 | 9 | import { useRouter } from 'next/router'; 10 | import { toast } from 'react-toastify'; 11 | 12 | import { useDispatch } from 'react-redux'; 13 | import { clearProductToEdit } from '../../../../store/modules/admin/product/reducer'; 14 | 15 | 16 | const Edit: React.FC = () => { 17 | const router = useRouter(); 18 | const dispatch = useDispatch(); 19 | 20 | const handleSubmit = async (product: FormData): Promise => { 21 | try { 22 | await ProductsService.update(product); 23 | 24 | toast.info('Produto atualizado com sucesso!'); 25 | 26 | // limpando o produto para edição do redux 27 | dispatch(clearProductToEdit()); 28 | router.back(); 29 | } catch (err) { 30 | toast.error('Ocorreu um erro ao atualizar o produto, tente novamente.'); 31 | console.log(err); 32 | } 33 | } 34 | 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | export default withAuthAdmin(Edit); -------------------------------------------------------------------------------- /pages/Admin/Products/New/index.tsx: -------------------------------------------------------------------------------- 1 | import AdminComponent from '../../../../components/shared/AdminComponent'; 2 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 3 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 4 | 5 | import ProductForm from '../../../../components/Admin/ProductForm'; 6 | 7 | import { toast } from 'react-toastify'; 8 | import { useRouter } from 'next/router'; 9 | import ProductsService from '../../../../services/products'; 10 | 11 | const New: React.FC = () => { 12 | const router = useRouter(); 13 | 14 | const handleSubmit = async (product: FormData): Promise => { 15 | try { 16 | if (!product.get('product[image]')) { 17 | toast.info('A imagem do produto é obrigatória!'); 18 | return; 19 | } 20 | 21 | await ProductsService.create(product); 22 | 23 | toast.info('Produto salvo com sucesso!'); 24 | router.back(); 25 | } catch (err) { 26 | toast.error('Ocorreu um erro ao salvar o produto, tente novamente.'); 27 | console.log(err); 28 | } 29 | } 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/SystemRequirements/Edit/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import SystemRequirementForm from '../../../../components/Admin/SystemRequirementForm'; 8 | import SystemRequirement from '../../../../dtos/SystemRequirement'; 9 | 10 | import { toast } from 'react-toastify'; 11 | import { useRouter } from 'next/router'; 12 | 13 | import { useDispatch } from 'react-redux'; 14 | import { clearSystemRequirementToEdit } from "../../../../store/modules/admin/systemRequirement/reducer"; 15 | 16 | import SystemRequirementsService from '../../../../services/systemRequirements'; 17 | 18 | const New: React.FC = () => { 19 | const router = useRouter(); 20 | const dispatch = useDispatch(); 21 | 22 | const handleSubmit = async (system_requirement: SystemRequirement): Promise => { 23 | try { 24 | await SystemRequirementsService.update(system_requirement); 25 | 26 | toast.info('Requisito de sistema atualizado com sucesso!'); 27 | dispatch(clearSystemRequirementToEdit()); 28 | router.back(); 29 | } catch (err) { 30 | toast.error('Erro ao atualizar o requisito de sistema, tente novamente.'); 31 | console.log(err); 32 | } 33 | } 34 | 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/SystemRequirements/New/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import SystemRequirementForm from '../../../../components/Admin/SystemRequirementForm'; 6 | 7 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 8 | import SystemRequirement from '../../../../dtos/SystemRequirement'; 9 | 10 | import { toast } from 'react-toastify'; 11 | import { useRouter } from 'next/router'; 12 | 13 | import SystemRequirementsService from '../../../../services/systemRequirements'; 14 | 15 | const New: React.FC = () => { 16 | const router = useRouter(); 17 | 18 | const handleSubmit = async (system_requirement: SystemRequirement): Promise => { 19 | try { 20 | await SystemRequirementsService.create(system_requirement); 21 | 22 | toast.info('Requisito de sistema salvo com sucesso!'); 23 | router.back(); 24 | } catch (err) { 25 | toast.error('Erro ao salvar o requisito de sistema, tente novamente.'); 26 | console.log(err); 27 | } 28 | } 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/Users/Edit/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import UserForm from '../../../../components/Admin/UserForm'; 8 | import User from '../../../../dtos/User'; 9 | 10 | import { toast } from 'react-toastify'; 11 | import { useRouter } from 'next/router'; 12 | import { useDispatch } from 'react-redux'; 13 | 14 | import { clearUserToEdit } from '../../../../store/modules/admin/user/reducer'; 15 | 16 | import UsersService from '../../../../services/users'; 17 | 18 | const Edit: React.FC = () => { 19 | const router = useRouter(); 20 | const dispatch = useDispatch(); 21 | 22 | const handleSubmit = async (user: User): Promise => { 23 | try { 24 | await UsersService.update(user); 25 | toast.info('Usuário atualizado com sucesso!'); 26 | dispatch(clearUserToEdit()); 27 | router.back(); 28 | } catch (err) { 29 | toast.error('Erro ao atualizar o usuário, tente novamente.'); 30 | console.log(err); 31 | } 32 | } 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | export default withAuthAdmin(Edit); -------------------------------------------------------------------------------- /pages/Admin/Users/List/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 5 | import { faEdit, faTrash, faUserPlus } from '@fortawesome/free-solid-svg-icons'; 6 | import AdminListTable from '../../../../components/shared/AdminListTable'; 7 | import AdminDeleteModal from '../../../../components/shared/AdminDeleteModal'; 8 | 9 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 10 | 11 | import { useRouter } from 'next/router'; 12 | import { useSelector, useDispatch } from 'react-redux'; 13 | 14 | import useSwr from 'swr'; 15 | import UsersService from '../../../../services/users'; 16 | 17 | import UrlService from '../../../../util/UrlService'; 18 | 19 | import { toast } from 'react-toastify'; 20 | import NoData from '../../../../components/shared/NoData'; 21 | 22 | import User from '../../../../dtos/User'; 23 | import { setUserToEdit } from '../../../../store/modules/admin/user/reducer'; 24 | 25 | const defaultUrl = '/admin/v1/users'; 26 | 27 | const List: React.FC = () => { 28 | const [show, setShow] = useState(false); 29 | const [url, setUrl] = useState(defaultUrl); 30 | const [userToRemove, setUserToRemove] = useState(0); 31 | 32 | const { data, error, mutate } = useSwr(url, UsersService.index); 33 | 34 | const search = useSelector(state => state.search); 35 | const router = useRouter(); 36 | const dispatch = useDispatch(); 37 | 38 | useEffect(() => { 39 | setUrl( 40 | defaultUrl + 41 | UrlService.execute({ page: router.query.page, search }) 42 | ); 43 | }, [search, router.query.page]); 44 | 45 | const handleShow = (id: number): void => { 46 | setShow(true); 47 | setUserToRemove(id); 48 | } 49 | 50 | const handleClose = async (success: boolean): Promise => { 51 | setShow(false); 52 | 53 | if (!success) return; 54 | 55 | try { 56 | await UsersService.delete(userToRemove); 57 | toast.info('Usuário removido com sucesso!'); 58 | mutate(); 59 | } catch (err) { 60 | toast.error('Erro ao remove o usuário, tente novamente.'); 61 | console.log(err); 62 | } 63 | } 64 | 65 | const handleEdit = (user: User): void => { 66 | dispatch(setUserToEdit(user)); 67 | router.push('/Admin/Users/Edit'); 68 | } 69 | 70 | if (error) { 71 | toast.error('Erro ao listar usuários!'); 72 | console.log(error); 73 | } 74 | 75 | return ( 76 | 77 | 83 | 84 | 85 | 86 | { 87 | data && data.users && data.users.length > 0 ? ( 88 | 95 | { 96 | data.users.map(user => ( 97 | 98 | {user.name} 99 | {user.email} 100 | {user.id} 101 | {user.profile === 'admin' ? 'Administrador' : 'Cliente'} 102 | 103 |
104 | handleEdit(user)} 107 | /> 108 |
109 | 110 | 111 |
112 | handleShow(user.id)} 115 | /> 116 |
117 | 118 | 119 | )) 120 | } 121 |
122 | ) : ( 123 | 124 | ) 125 | } 126 | 127 |
128 | ) 129 | } 130 | 131 | export default withAuthAdmin(List); -------------------------------------------------------------------------------- /pages/Admin/Users/New/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../../../components/shared/AdminComponent'; 3 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 4 | 5 | import withAuthAdmin from '../../../../components/withAuthAdmin'; 6 | 7 | import UserForm from '../../../../components/Admin/UserForm'; 8 | import User from '../../../../dtos/User'; 9 | 10 | import { toast } from 'react-toastify'; 11 | import { useRouter } from 'next/router'; 12 | 13 | import UsersService from '../../../../services/users'; 14 | 15 | const New: React.FC = () => { 16 | const router = useRouter(); 17 | 18 | const handleSubmit = async (user: User): Promise => { 19 | if (user.password !== user.password_confirmation) { 20 | toast.error('A senha e a confirmação de senha devem ser iguais!') 21 | return; 22 | } 23 | 24 | try { 25 | await UsersService.create(user); 26 | toast.info('Usuário salvo com sucesso!'); 27 | router.back(); 28 | } catch (err) { 29 | toast.error('Erro ao salvar o usuário, tente novamente.'); 30 | console.log(err); 31 | } 32 | } 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | export default withAuthAdmin(New); -------------------------------------------------------------------------------- /pages/Admin/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AdminComponent from '../../components/shared/AdminComponent'; 3 | import withAuthAdmin from '../../components/withAuthAdmin'; 4 | 5 | import Dashboard from '../../components/Admin/Dashboard'; 6 | 7 | const Home: React.FC = () => { 8 | return ( 9 | 10 | 11 | 12 | ) 13 | } 14 | 15 | export default withAuthAdmin(Home); -------------------------------------------------------------------------------- /pages/Auth/ChangePassword/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { InputGroup, FormControl, Button } from 'react-bootstrap'; 3 | import PasswordComponent from '../../../components/shared/PasswordComponent'; 4 | import MainComponent from '../../../components/shared/MainComponent'; 5 | 6 | const ChangePassword: React.FC = () => { 7 | return ( 8 | 9 | 10 |
Criar nova senha
11 | 12 |
13 | 14 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 |
27 |
28 |
29 | ) 30 | } 31 | 32 | export default ChangePassword; -------------------------------------------------------------------------------- /pages/Auth/Login/index.tsx: -------------------------------------------------------------------------------- 1 | import LoginForm from '../../../components/LoginForm'; 2 | import MainComponent from '../../../components/shared/MainComponent'; 3 | import SignUpForm from '../../../components/SignUpForm'; 4 | 5 | const Login: React.FC = () => { 6 | return ( 7 | 8 |
9 |

Entrar

10 | 11 | 12 | 13 |
14 | 15 | 16 |
17 |
18 | ) 19 | } 20 | 21 | export default Login; -------------------------------------------------------------------------------- /pages/Auth/PasswordRecovery/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { InputGroup, FormControl, Button } from 'react-bootstrap'; 3 | import MainComponent from '../../../components/shared/MainComponent'; 4 | import PasswordComponent from '../../../components/shared/PasswordComponent'; 5 | 6 | const PasswordRecovery: React.FC = () => { 7 | return ( 8 | 9 | 10 |
11 |
Digite o Email Cadastrado
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | ) 22 | } 23 | 24 | export default PasswordRecovery; -------------------------------------------------------------------------------- /pages/Cart/styles.module.css: -------------------------------------------------------------------------------- 1 | .product { 2 | margin-top: 30px; 3 | } 4 | 5 | .product img { 6 | display: block; 7 | margin: 0 auto; 8 | } 9 | 10 | .title { 11 | font-size: 1.2em; 12 | font-weight: 600; 13 | } 14 | 15 | .icon { 16 | cursor: pointer; 17 | transition: color 0.2s; 18 | } 19 | 20 | .icon:hover { 21 | color: var(--color-hover); 22 | } 23 | 24 | .icon:active { 25 | color: var(--color-active); 26 | } 27 | 28 | .gray_input { 29 | background-color: var(--color-secondary); 30 | border: none; 31 | padding: 5px; 32 | margin-right: 10px; 33 | border-radius: 5px; 34 | color: white; 35 | } 36 | 37 | .gray_select { 38 | background-color: var(--color-secondary); 39 | border: none; 40 | padding: 5px; 41 | border-radius: 5px; 42 | color: white; 43 | width: 100%; 44 | } 45 | 46 | .gray_button { 47 | background-color: var(--color-secondary); 48 | border: none; 49 | } 50 | 51 | .payment div { 52 | margin-right: 5px !important; 53 | margin-top: 5px !important; 54 | } 55 | 56 | .payment_column { 57 | margin-top: 20px; 58 | } 59 | 60 | .back_button { 61 | height: 50px; 62 | } 63 | 64 | .line { 65 | background-color: white; 66 | opacity: 0.2; 67 | } 68 | 69 | .primary_badge { 70 | opacity: 0.6; 71 | background-color: var(--color-secondary); 72 | } 73 | 74 | .coupon { 75 | margin-bottom: 20px; 76 | } 77 | 78 | .price_and_discount { 79 | text-align: right; 80 | } 81 | 82 | .blue_text { 83 | color: var(--color-blue-light); 84 | text-align: right; 85 | padding-left: 2px; 86 | } -------------------------------------------------------------------------------- /pages/Games/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { Row, Col } from 'react-bootstrap'; 3 | import MainComponent from '../../components/shared/MainComponent'; 4 | import Menu from '../../components/Storefront/Menu'; 5 | import Product from '../../components/Storefront/Product'; 6 | 7 | import ProductLicensesModal from '../../components/Storefront/ProductLicensesModal'; 8 | import withAuth from '../../components/withAuth'; 9 | 10 | import GamesService from '../../services/games'; 11 | import useSwr from 'swr'; 12 | import { toast } from 'react-toastify'; 13 | 14 | import Game from '../../dtos/Game'; 15 | 16 | const Games: React.FC = () => { 17 | const[show, setShow] = useState(false); 18 | const[selectedProduct, setSelectedProduct] = useState(); 19 | 20 | const { data, error } = useSwr('/storefront/v1/games', GamesService.index); 21 | 22 | if (error) { 23 | toast.error('Erro ao obter os seus jogos.'); 24 | console.log(error); 25 | } 26 | 27 | const handleProductClick = (id: number): void => { 28 | handleShow(); 29 | setSelectedProduct(data?.find(game => game.id === id)); 30 | } 31 | 32 | const handleShow = (): void => { 33 | setShow(!show); 34 | } 35 | 36 | return ( 37 | 38 | 39 | 40 | 41 | { 42 | data?.map( 43 | product => 44 | 50 | handleProductClick(product?.id)} 53 | /> 54 | 55 | ) 56 | } 57 | 58 | 59 | 64 | 65 | ); 66 | } 67 | 68 | export default withAuth(Games); -------------------------------------------------------------------------------- /pages/Orders/List/index.tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from "../../../components/shared/MainComponent"; 2 | import Menu from "../../../components/Storefront/Menu"; 3 | import BlueBackground from "../../../components/shared/BlueBackground"; 4 | 5 | import OrderService from "../../../services/order"; 6 | import useSWR from "swr"; 7 | 8 | import { toast } from "react-toastify"; 9 | import withAuth from "../../../components/withAuth"; 10 | import OrdersList from "../../../components/shared/OrdersList"; 11 | 12 | const Orders: React.FC = () => { 13 | const { data, error } = useSWR('/storefront/v1/orders', OrderService.index); 14 | 15 | if (error) { 16 | toast.error('Erro ao obter os dados dos pedidos!'); 17 | console.log(error); 18 | } 19 | 20 | return ( 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 | ) 31 | } 32 | 33 | export default withAuth(Orders); -------------------------------------------------------------------------------- /pages/Orders/[id].tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from "../../components/shared/MainComponent"; 2 | import StyledButton from "../../components/shared/StyledButton"; 3 | 4 | import styles from './styles.module.css'; 5 | 6 | import { faArrowLeft } from "@fortawesome/free-solid-svg-icons"; 7 | import { useRouter } from 'next/router'; 8 | 9 | import withAuth from "../../components/withAuth"; 10 | import OrderDetail from "../../components/shared/OrderDetail"; 11 | import OrderService from "../../services/order"; 12 | 13 | import useSwr from 'swr'; 14 | import { toast } from "react-toastify"; 15 | 16 | const Order: React.FC = () => { 17 | const router = useRouter(); 18 | const { id } = router?.query; 19 | 20 | const { data, error } = useSwr( 21 | () => id ? `/storefront/v1/orders/${id}` : null, OrderService.show 22 | ); 23 | 24 | if (error) { 25 | toast.error('Erro ao obter os dados do pedido!'); 26 | console.log(error); 27 | } 28 | 29 | return ( 30 | 31 |
32 | Pedido 33 | {` #${data?.id}`} 34 | 35 |
36 | router.back()} 42 | /> 43 |
44 |
45 | 46 | 47 |
48 | ); 49 | } 50 | 51 | export default withAuth(Order); -------------------------------------------------------------------------------- /pages/Orders/styles.module.css: -------------------------------------------------------------------------------- 1 | .secondary_color { 2 | color: var(--color-gray-light); 3 | } 4 | 5 | .button_blue_light { 6 | color: var(--color-secondary); 7 | background-color: var(--color-blue-light); 8 | } 9 | 10 | .blue_text { 11 | color: var(--color-blue-light); 12 | } -------------------------------------------------------------------------------- /pages/PaymentConfirmation/[id].tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from "../../components/shared/MainComponent"; 2 | import StyledButton from "../../components/shared/StyledButton"; 3 | 4 | import styles from './styles.module.css'; 5 | 6 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 7 | import { faArrowLeft, faGamepad, faCheckSquare } from "@fortawesome/free-solid-svg-icons"; 8 | 9 | import { Col, Row } from "react-bootstrap"; 10 | 11 | import withAuth from "../../components/withAuth"; 12 | 13 | import useSwr from 'swr'; 14 | import { useRouter } from 'next/router'; 15 | 16 | import OrderService from "../../services/order"; 17 | import { toast } from "react-toastify"; 18 | 19 | import OrderDetail from '../../components/shared/OrderDetail'; 20 | 21 | const PaymentConfirmation: React.FC = () => { 22 | const router = useRouter(); 23 | 24 | const { id } = router.query; 25 | 26 | const { data, error } = useSwr( 27 | () => id ? `/storefront/v1/orders/${id}` : null, OrderService.show 28 | ); 29 | 30 | if (error) { 31 | toast.error('Erro ao obter os dados do pedido!'); 32 | console.log(error); 33 | } 34 | 35 | return ( 36 | 37 |
38 | Pagamento Recebido 39 | {` #${data?.id}`} 40 | 41 |
42 | router.push('/')} 47 | type="button" 48 | /> 49 |
50 |
51 | 52 |
53 |
54 | 55 | Sucesso: 56 | Obrigado por seu pedido! Você receberá um e-mail em breve. 57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 72 |
73 | ) 74 | } 75 | 76 | export default withAuth(PaymentConfirmation); -------------------------------------------------------------------------------- /pages/PaymentConfirmation/styles.module.css: -------------------------------------------------------------------------------- 1 | .secondary_color { 2 | color: var(--color-gray-light); 3 | } 4 | 5 | .button_blue_light { 6 | color: var(--color-secondary); 7 | background-color: var(--color-blue-light); 8 | } 9 | 10 | .blue_text { 11 | color: var(--color-blue-light); 12 | } -------------------------------------------------------------------------------- /pages/Product/styles.module.css: -------------------------------------------------------------------------------- 1 | .primary_badge { 2 | opacity: 0.6; 3 | background-color: var(--color-secondary); 4 | } 5 | 6 | .secondary_badge { 7 | background-color: var(--color-secondary); 8 | } 9 | 10 | .line { 11 | background-color: white; 12 | opacity: 0.2; 13 | } 14 | 15 | .title { 16 | font-size: 1.2em; 17 | font-weight: 600; 18 | } 19 | 20 | .subtitle { 21 | font-weight: 600; 22 | } 23 | 24 | .gray_button { 25 | background-color: var(--color-secondary); 26 | border: none; 27 | } 28 | 29 | .list { 30 | margin-top: 20px; 31 | padding-left: 0; 32 | } 33 | 34 | .list li { 35 | display: block; 36 | } 37 | 38 | .list li span { 39 | margin-left: 5px 40 | } 41 | 42 | .mb_50 { 43 | margin-bottom: 50px; 44 | } 45 | 46 | .mb_50 p { 47 | margin-bottom: 0; 48 | } -------------------------------------------------------------------------------- /pages/Profile/styles.module.css: -------------------------------------------------------------------------------- 1 | .blue_text { 2 | color: var(--color-blue-light); 3 | } 4 | 5 | .input_background { 6 | background-color: var(--color-secondary); 7 | border: none; 8 | border-radius: 5px; 9 | } 10 | 11 | .input_background:focus { 12 | background-color: var(--color-secondary); 13 | } 14 | 15 | .form input, 16 | .form input:focus { 17 | color: white; 18 | } -------------------------------------------------------------------------------- /pages/Search/styles.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | background: none; 3 | border: none; 4 | } 5 | 6 | .button:hover { 7 | color: var(--color-hover); 8 | background: none; 9 | border: none; 10 | } 11 | 12 | .search_icon { 13 | cursor: pointer; 14 | transition: color 0.2s; 15 | } 16 | 17 | .search_icon:hover { 18 | color: var(--color-hover); 19 | } 20 | 21 | .search_icon:active { 22 | color: var(--color-active); 23 | } 24 | 25 | .results { 26 | margin: 10px 0; 27 | text-align: left; 28 | } 29 | 30 | .ordenation { 31 | margin: 10px 0; 32 | text-align: right; 33 | } 34 | 35 | .primary { 36 | background: var(--color-primary); 37 | border: none; 38 | border-radius: 0; 39 | margin: 0 5px; 40 | padding: 5px; 41 | color: white; 42 | } 43 | 44 | .secondary { 45 | background: var(--color-secondary); 46 | border: 1px solid white; 47 | padding: 5px; 48 | color: white; 49 | } 50 | 51 | .pagination { 52 | margin-bottom: 20px; 53 | } 54 | 55 | .pagination div { 56 | justify-content: center; 57 | } 58 | 59 | @media screen and (max-width: 991px) { 60 | .title { 61 | font-size: 1.2em; 62 | margin-bottom: 15px; 63 | } 64 | 65 | .ordenation { 66 | text-align: left; 67 | } 68 | 69 | .primary, 70 | .secondary { 71 | width: 100%; 72 | } 73 | 74 | .primary { 75 | margin: 5px 15px; 76 | } 77 | } 78 | 79 | @media screen and (max-width: 575px) { 80 | .results { 81 | text-align: center; 82 | } 83 | } -------------------------------------------------------------------------------- /pages/Wishlist/index.tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from '../../components/shared/MainComponent'; 2 | 3 | import Menu from '../../components/Storefront/Menu'; 4 | 5 | import useSwr from 'swr'; 6 | import WishlistService from '../../services/wishlist'; 7 | 8 | import { toast } from 'react-toastify'; 9 | 10 | import WishItem from '../../components/Storefront/WishItem'; 11 | 12 | const Wishlist: React.FC = () => { 13 | const { data, error, mutate } = useSwr('/storefront/v1/wish_items', WishlistService.index); 14 | 15 | if (error) { 16 | toast.error('Erro ao obter os games desejados.'); 17 | console.log(error); 18 | } 19 | 20 | const handleWishlistItemRemoval = async (productId: number): Promise => { 21 | try { 22 | await WishlistService.remove(productId); 23 | toast.info('Item removido da sua lista de desejos!'); 24 | mutate(); 25 | } catch (error) { 26 | toast.error('Erro ao remover item da sua lista de desejos.') 27 | console.log(error); 28 | } 29 | } 30 | 31 | return ( 32 | 33 | 34 | 35 | { 36 | data?.wish_items?.map( 37 | wish_item => 38 | 43 | ) 44 | } 45 | 46 | ); 47 | } 48 | 49 | export default Wishlist; -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { AppProps } from 'next/app'; 2 | import Head from 'next/head'; 3 | import 'bootstrap/dist/css/bootstrap.min.css'; 4 | import '../styles/globals.css'; 5 | 6 | import { Provider } from 'react-redux'; 7 | import { PersistGate } from 'redux-persist/integration/react'; 8 | import { store, persistor } from '../store'; 9 | 10 | import { ToastContainer } from 'react-toastify'; 11 | import 'react-toastify/dist/ReactToastify.css'; 12 | 13 | const MyApp: React.FC = ({ Component, pageProps }) => { 14 | return ( 15 | 16 | 17 |
18 | 19 | OneBitGames 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 | ) 29 | } 30 | 31 | export default MyApp 32 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import MainComponent from '../components/shared/MainComponent'; 2 | import { Carousel } from 'react-bootstrap'; 3 | import styles from './styles.module.css'; 4 | import HightlightedProducts from '../components/Storefront/HighlightedProducts'; 5 | 6 | import useSwr from 'swr'; 7 | import HomeService from '../services/home'; 8 | 9 | import { toast } from 'react-toastify'; 10 | 11 | import { useRouter } from 'next/router'; 12 | 13 | import HomeIndexData from '../dtos/HomeIndexData'; 14 | 15 | interface StorefrontProps { 16 | products: HomeIndexData; 17 | } 18 | 19 | const Storefront: React.FC = ({ products }) => { 20 | const { data, error } = useSwr( 21 | '/storefront/v1/home', 22 | HomeService.index, 23 | { initialData: products } 24 | ); 25 | const { featured, last_releases, cheapest } = data; 26 | const router = useRouter(); 27 | 28 | if (error) { 29 | toast.error('Erro ao obter dados da home!'); 30 | console.log(error); 31 | } 32 | 33 | return ( 34 | 35 | 36 | { 37 | featured?.slice(0, 3)?.map( 38 | product => ( 39 | router.push(`/Product/${product.id}`)} 42 | className={styles.carousel_item} 43 | > 44 | {product.name} 49 | 50 | ) 51 | ) 52 | } 53 | 54 | 55 | router.push({ 61 | pathname: '/Search', 62 | query: { 63 | order: 'price', 64 | direction: 'asc' 65 | } 66 | }) 67 | } 68 | /> 69 | 70 | router.push({ 75 | pathname: '/Search', 76 | query: { 77 | order: 'release_date', 78 | direction: 'desc' 79 | } 80 | }) 81 | } 82 | /> 83 | 84 | router.push({pathname: '/Search'}) 89 | } 90 | /> 91 | 92 | ) 93 | } 94 | 95 | export async function getStaticProps(context) { 96 | const products = await HomeService.index('/storefront/v1/home'); 97 | return { props: { products } } 98 | } 99 | 100 | export default Storefront; -------------------------------------------------------------------------------- /pages/styles.module.css: -------------------------------------------------------------------------------- 1 | .carousel { 2 | margin-top: 20px; 3 | width: 80%; 4 | margin-left: 10%; 5 | } 6 | 7 | .carousel_image { 8 | max-height: 499.5px; 9 | } 10 | 11 | .carousel_item { 12 | cursor: pointer; 13 | } -------------------------------------------------------------------------------- /public/assets/card-flags/boleto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/card-flags/boleto.png -------------------------------------------------------------------------------- /public/assets/card-flags/mastercard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/card-flags/mastercard.png -------------------------------------------------------------------------------- /public/assets/card-flags/visa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/card-flags/visa.png -------------------------------------------------------------------------------- /public/assets/logo-bootcamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/logo-bootcamp.png -------------------------------------------------------------------------------- /public/assets/logo-games.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/logo-games.png -------------------------------------------------------------------------------- /public/assets/product_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/assets/product_image.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-frontend-Dev/6574c4996cebccb16ea4d9a4f5f2c83cc0807c16/public/favicon.ico -------------------------------------------------------------------------------- /public/snippets/1-criando_nosso_app.md: -------------------------------------------------------------------------------- 1 | # Criando e organizando nosso projeto 2 | 3 | # 1. Introdução 4 | 5 | Nessa aula, vamos criar e configurar o básico do nosso app! Aqui vamos deixar o app bem preparado para receber nosso conteúdo. 6 | 7 | ## Dependências: 8 | 9 | 1 - Instale o node + npm via asdf: 10 | 11 | ```shell 12 | asdf plugin-add nodejs 13 | brew install gpg 14 | bash ~/.asdf/plugins/nodejs/bin/import-release-team-keyring 15 | asdf install nodejs 15.0.1 16 | asdf global nodejs 15.0.1 17 | node -v 18 | ``` 19 | 20 | 2 - Instale o Yarn rodando: 21 | 22 | ``` 23 | npm install --global yarn 24 | ``` 25 | 26 | ### Create-next-app 27 | 28 | Para instalar o comando "create-next-app" rode o seguinte comando: 29 | 30 | ``` 31 | npm i create-next-app 32 | ``` 33 | 34 | # 2. Conteúdo 35 | 36 | 1. Crie o app com o seguinte comando: 37 | 38 | ``` 39 | npx create-next-app web 40 | ``` 41 | 42 | 2. E exclua a pasta "api" dentro da pasta "pages", pois não usaremos ela em nosso app, sendo um web application. 43 | 44 | 3. Instale o typescript no seu projeto com o seguinte comando: 45 | ``` 46 | npm install typescript @types/react -D 47 | ``` 48 | 49 | 4. Troque as extensões dos arquivos .js para .tsx, pois agora vamos começarmos a utilizar o typescript. 50 | 51 | 6. No arquivo o arquivo pages/_app, adicione a estrutura do typescript: 52 | 53 | > Utilizamos o aqui para setar os atributos do cabeçalho da nossa página em todas as páginas. 54 | 55 | ```jsx 56 | ... 57 | import React from 'react'; 58 | import { AppProps } from 'next/app'; 59 | 60 | const MyApp: React.FC = ({ Component, pageProps }) => { 61 | ... 62 | ``` 63 | 64 | 7. Agora vamos adicionar nosso Head, à nossa page_app.tsx: 65 | 66 | >Ele será responsável pelo Head da nossa página. Aqui colocaremos futuramente toda a parte de CEO dele, meta tags, etc. 67 | 68 | ```jsx 69 | ... 70 | import Head from 'next/head'; 71 | 72 | ... 73 | return ( 74 | <> 75 | 76 | OneBitGames 77 | 78 | 79 | 80 | 81 | 82 | ) 83 | ... 84 | ``` 85 | 86 | 8. Agora deixe a aparência do arquivo pages/index.tsx do seguinte modo: 87 | ```jsx 88 | export default function Home() { 89 | return

Hello World

90 | } 91 | ``` 92 | 93 | 9. E vamos adicionar a estrutura do typescript nele: 94 | 95 | ```jsx 96 | import React from 'react'; 97 | 98 | ... 99 | const Home: React.FC = () => { 100 | ... 101 | 102 | export default Home; 103 | ``` 104 | 105 | 10. No arquivo package.json na raiz do projeto, modifique o script "dev" por: 106 | 107 | ```jsx 108 | "dev": "next dev -p 3001", 109 | ``` 110 | 111 | 11. Rode o projeto com o seguinte comando: 112 | ``` 113 | npm run dev 114 | ``` -------------------------------------------------------------------------------- /public/snippets/10-criando_menu_lateral.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar o menu lateral do nosso painel do admin. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta components/shared/LateralMenu, e crie o arquivo index.tsx com o seguinte conteúdo: 8 | 9 | ```jsx 10 | import React from 'react'; 11 | 12 | const LateralMenu: React.FC = () => { 13 | return( 14 |
15 | Menu Lateral 16 |
17 | ) 18 | } 19 | 20 | export default LateralMenu; 21 | ``` 22 | 23 | 2. Crie o arquivo styles/MenuLateral.module.css e adicione o seguinte conteúdo: 24 | 25 | ```css 26 | .background { 27 | background-color: var(--color-primary); 28 | padding: 40px; 29 | height: 100%; 30 | } 31 | 32 | .list { 33 | margin-top: 50px; 34 | } 35 | ``` 36 | 37 | 3. Agora vamos colocar nossa logo no menu lateral: 38 | 39 | ```jsx 40 | import Logo from '../Logo'; 41 | ... 42 |
43 | 44 | 45 |
46 | Lista do menu 47 |
48 |
49 | ``` 50 | 51 | 4. Agora vamos colocar nossos ícones no menu lateral: 52 | 53 | ```jsx 54 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 55 | import { faSignal, faUser, faGamepad, faCheckSquare, faLaptop, faTicketAlt, faDollarSign, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; 56 | 57 | ... 58 |
59 | 60 | Painel Inicial 61 |
62 | 63 | 64 | Usuários 65 |
66 | 67 | 68 | Produtos 69 |
70 | 71 | 72 | Categorias 73 |
74 | 75 | 76 | Requisitos do sistema 77 |
78 | 79 | 80 | Cupons 81 |
82 | 83 | 84 | Financeiro 85 |
86 | 87 | 88 | Sair 89 |
90 |
91 | ... 92 | ``` 93 | 94 | 5. Agora vamos colocar nosso menu lateral no nosso AdminComponent. Acesse o components/shared/AdminComponent e insira o seguinte código: 95 | 96 | ```jsx 97 | import LateralMenu from '../LateralMenu'; 98 | ... 99 | 100 | 101 | 102 | ``` -------------------------------------------------------------------------------- /public/snippets/12-criando_detalhes_do_usuario(admin).md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página de detalhes do usuário. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta pages/Admin/Users/Details, e dentro crie o arquivo index.tsx com o seguinte conteúdo: 8 | 9 | ```jsx 10 | import React from 'react'; 11 | 12 | const Details: React.FC = () => { 13 | return ( 14 | <> 15 | 16 | 17 | ) 18 | } 19 | 20 | export default Details; 21 | ``` 22 | 23 | 2. Agora vamos envolver com nosso AdminComponent: 24 | 25 | ```jsx 26 | import AdminComponent from '../../../../components/shared/AdminComponent'; 27 | ... 28 | 29 | ... 30 | 31 | 32 | ... 33 | ``` 34 | 35 | 3. Coloque também nosso TitleAdminPanel: 36 | 37 | ```jsx 38 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 39 | ... 40 | 41 | ``` 42 | 43 | 4. Agora vamos colocar nosso detalhe do usuário e nossos botões: 44 | 45 | ```jsx 46 | import { Col, Row } from 'react-bootstrap'; 47 | import { faTrash, faEdit } from '@fortawesome/free-solid-svg-icons'; 48 | import StyledButton from '../../../../components/shared/StyledButton'; 49 | import styles from '../../../../styles/AdminPanel.module.css'; 50 | ... 51 |
52 | 53 | 54 |
Nome: Leonardo Scorza
55 |
E-mail: contato@onebitcode.com
56 | 57 | 58 | 59 |
ID: #00001
60 |
Status: Administrador
61 | 62 |
63 | 64 |
65 | 66 | 67 |
68 |
69 | ... 70 | ``` 71 | 72 | 5. No nosso style/AdminPanel.module.css adicione o seguinte conteúdo: 73 | 74 | ```css 75 | ... 76 | 77 | .details_button { 78 | text-align: right; 79 | } 80 | 81 | .details_button > button { 82 | margin: 10px; 83 | } 84 | ``` -------------------------------------------------------------------------------- /public/snippets/13-criando_pagina_para_adicionar_user.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para adicionar novos usuários. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Users/New, e dentro crie o arquivo index.tsx com o seguinte conteúdo: 8 | 9 | ```jsx 10 | import React from 'react'; 11 | 12 | const New: React.FC = () => { 13 | return ( 14 | <> 15 | 16 | 17 | ) 18 | } 19 | 20 | export default New; 21 | ``` 22 | 23 | 2. Vamos envolver nosso conteúdo com o AdminComponent, e coloque o componente TitleAdminPage: 24 | 25 | ```jsx 26 | import AdminComponent from '../../../../components/shared/AdminComponent'; 27 | import TitleAdminPanel from '../../../../components/shared/TitleAdminPanel'; 28 | ... 29 | 30 | 31 | 32 | 33 | ... 34 | ``` 35 | 36 | 3. No arquivo styles/AdminPanel.module.css, adicione o seguinte código: 37 | 38 | ```css 39 | .new_form { 40 | text-align: left; 41 | padding: 20px; 42 | } 43 | 44 | .secundary_input, .secundary_input:focus { 45 | background-color: var(--color-background); 46 | color: white; 47 | border: none; 48 | } 49 | ``` 50 | 51 | 4. Volte na page Users/New, e coloque o seguinte conteúdo: 52 | 53 | >Vamos adicionar o nosso formulário na página 54 | 55 | ```jsx 56 | import { Form, Col } from 'react-bootstrap'; 57 | import { faUserPlus, faTimes } from '@fortawesome/free-solid-svg-icons'; 58 | import styles from '../../../../styles/AdminPanel.module.css'; 59 | ... 60 |
61 |
62 | 63 | 64 | Nome 65 | 66 | 67 | 68 | 69 | ID 70 | 71 | 72 | 73 | 74 | 75 | 76 | Email 77 | 78 | 79 | 80 | 81 | Status 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 | Botões 91 |
92 | ... 93 | ``` 94 | 95 | 5. Agora, substitua o nome "botões" e adicione o seguinte conteúdo: 96 | 97 | ```jsx 98 | import StyledButton from '../../../../components/shared/StyledButton'; 99 | ... 100 | 101 | 102 |
103 | 104 | 105 |
106 | ... 107 | ``` -------------------------------------------------------------------------------- /public/snippets/14-criando_pagina_para_editar_usuario.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para editar usuários. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Users/Edit, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/New para a página de Edit e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | ... 13 | import { faUser, faTimes } from '@fortawesome/free-solid-svg-icons'; 14 | ... 15 | 16 | ... 17 | 18 | 19 | ... 20 | ``` -------------------------------------------------------------------------------- /public/snippets/15-criando_list_de_produtos.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para listar produtos. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Products/List, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/List para a página de listar produtos e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | ... 13 | 14 | 15 | 16 | ... 17 | ``` 18 | 19 | 3. Agora modifique a listagem de produtos para: 20 | 21 | ```jsx 22 | ... 23 | 24 | 25 | Ri sem dente evil 26 | Terror, Suspense, História 27 | #000001 28 | Disponível 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | Cuphead 37 | Ação, Desenho 38 | #000002 39 | Disponível 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | Gran Turismo 48 | Corrida, Esportes 49 | #000003 50 | Indisponível 51 | 52 | 53 | 54 | 55 |
56 | 57 | 58 | Far Cry 4 59 | História, Aventura, Mundo Aberto 60 | #000003 61 | Disponível 62 | 63 | 64 | 65 |
66 | ... 67 | ``` -------------------------------------------------------------------------------- /public/snippets/16-criando-details_do_produto.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para exibir os detalhes de um produto. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Products/Details, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/Details para a página de listar produtos e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | ... 13 | 14 | ... 15 | 16 |
Nome: Far Cry 4
17 |
Categorias: História, Aventura, Mundo Aberto, Ação, Estratégia.
18 | 19 | 20 | 21 |
Código: #00001
22 |
Status: Disponível
23 | 24 | ... 25 | ``` 26 | 27 | 3. Agora vamos personalizar nossa página, usando bootstrap, da seguinte forma: 28 | 29 | ```jsx 30 | ... 31 |
32 | 33 | 34 | ... 35 | 36 | 37 | 38 | 39 | 40 |
Nome: Far Cry 4
41 |
Categorias: História, Aventura, Mundo Aberto, Ação, Estratégia.
42 | 43 | 44 | 45 |
Código: #00001
46 |
Status: Disponível
47 | 48 |
49 | 50 |
51 | ... 52 | ``` 53 | 54 | 5. Agora vamos adicionar nossa imagem no da seguinte forma: 55 | 56 | ```jsx 57 | import Image from 'next/image'; 58 | ... 59 | Logo Bootcamp 60 | ... 61 | ``` -------------------------------------------------------------------------------- /public/snippets/17-criando_novo_produto.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para criar um novo produto. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Products/New, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/New para a página de listar produtos e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | import { Form, Col, Row } from 'react-bootstrap'; 13 | import { faTimes, faArrowUp, faTrash, faGamepad } from '@fortawesome/free-solid-svg-icons'; 14 | ... 15 | 16 | ... 17 | 18 | 19 | ... 20 | ``` 21 | 22 | 3. Agora vamos personalizar nossa página, usando bootstrap, da seguinte forma: 23 | 24 | ```jsx 25 | ... 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ... 35 | ``` 36 | 37 | 4. Vamos começar a colocar nossa imagem. Na primeira separação do , adicione o seguinte: 38 | 39 | ```jsx 40 | import Image from 'next/image'; 41 | ... 42 | Logo Bootcamp 43 | 44 |
45 | 46 | 47 |
48 | ... 49 | ``` 50 | 51 | 5. Agora vamos construir nosso formulário. No , adicione o seguinte código: 52 | 53 | ```jsx 54 | ... 55 |
56 | 57 | 58 | 59 | 60 | 61 |
62 | ... 63 | ``` 64 | 65 | 6. No primeiro , vamos adicionar os nossos primeiros inputs: 66 | 67 | ```jsx 68 | ... 69 | 70 | Nome 71 | 72 | 73 | 74 | 75 | Código 76 | 77 | 78 | ... 79 | ``` 80 | 81 | 7. Agora no segundo , vamos colocar os nossos selects options: 82 | 83 | ```jsx 84 | 85 | Categorias 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Status 96 | 97 | 98 | 99 | 100 | 101 | ``` -------------------------------------------------------------------------------- /public/snippets/18-criando_pagina_para_editar_produto.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para editar um produto. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Products/Edit, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Products/New para a página de editar um produto e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | ... 13 | const Edit: React.FC = () => { 14 | ... 15 | 16 | ... 17 | 18 | ... 19 | export default Edit; 20 | ``` -------------------------------------------------------------------------------- /public/snippets/19-criando_list_de_categorias.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para listar nossas categorias. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Category/List, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Products/List para a página de listar categorias e modifique os seguintes pontos: 10 | 11 | ```jsx 12 | ... 13 | import { faEdit, faTrash, faGhost } from '@fortawesome/free-solid-svg-icons'; 14 | ... 15 | 16 | 17 | 18 | ... 19 | ``` 20 | 21 | 3. Agora vamos alterar nossa tabela. 22 | 23 | 4. Como as categorias só terão o atributo "nome", deixe-a da seguinte forma: 24 | 25 | ```jsx 26 | ... 27 | 28 | 29 | Terror 30 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | Suspense 38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | Mundo Aberto 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | Ação 54 | 55 | 56 | 57 |
58 | ... 59 | ``` -------------------------------------------------------------------------------- /public/snippets/2-instalando_o_bootstrap.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos instalar o bootstrap ao nosso app. 4 | 5 | # 2. Conteúdo 6 | 7 | ### Instalando o Bootstrap 8 | 9 | 1. Rode na linha de comando: 10 | ``` 11 | npm install react-bootstrap bootstrap 12 | ``` 13 | 14 | 2. No arquivo pages/_app.tsx adicione as seguintes importações: 15 | 16 | ``` 17 | import 'bootstrap/dist/css/bootstrap.min.css'; 18 | ``` 19 | ### Setando o CSS global 20 | 21 | 1. Crie o style /styles/globals.css, coloque o seguinte código setando as variáveis globais, a fonte padrão e a cor de texto padrão para white. 22 | 23 | ```css 24 | @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400&display=swap'); 25 | 26 | :root { 27 | --color-primary: #10163A; 28 | --color-secondary: #212744; 29 | --color-background: #262C49; 30 | --color-gray-light: #7DA1BC; 31 | --color-blue-light: #00CFFF; 32 | --color-white: #FFFFFF; 33 | } 34 | 35 | html, 36 | body { 37 | padding: 0; 38 | margin: 0; 39 | font-family: 'Raleway', sans-serif; 40 | background: var(--color-background); 41 | color: white; 42 | overflow-x: hidden; 43 | } 44 | 45 | body, .sticky-footer-wrapper { 46 | min-height:100vh; 47 | } 48 | ... 49 | ``` -------------------------------------------------------------------------------- /public/snippets/20-criando_details_da_categoria.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para detalhar uma categoria. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Category/Details, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Products/Details para a página de listar categorias e apague as seguintes importações: 10 | 11 | ```jsx 12 | ... 13 | import { Col, Row } from 'react-bootstrap'; 14 | ... 15 | import Image from 'next/image'; 16 | ... 17 | ``` 18 | 19 | 3. Modifique o TitleAdminPanel para a seguinte forma: 20 | 21 | ```jsx 22 | 23 | ``` 24 | 25 | 4. Apague todo o código abaixo do TitleAdminPanel, dentro do return, e substitua-o pelo seguinte: 26 | 27 | ```jsx 28 | ... 29 |
30 |
Nome: Mundo Aberto
31 | 32 |
33 | 34 | 35 |
36 |
37 | ... 38 | ``` -------------------------------------------------------------------------------- /public/snippets/21-criando_edit_de_categoria.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para editar uma categoria. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Category/Edit, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/Edit para a página de listar categorias e modifique os seguintes códigos: 10 | 11 | ```jsx 12 | ... 13 | import { Form } from 'react-bootstrap'; 14 | ... 15 | 16 | ... 17 | ``` 18 | 19 | 3. E deixe a
com a seguinte aparência: 20 | 21 | ```jsx 22 | ... 23 |
24 |
25 | Nome 26 | 27 | 28 | 29 |
30 | 31 | 32 |
33 |
34 | ... 35 | ``` -------------------------------------------------------------------------------- /public/snippets/22-criando_new_de_categorias.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar a página para editar uma categoria. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie a pasta page/Admin/Category/New, e dentro crie o arquivo index.tsx. 8 | 9 | 2. Copie o conteúdo do page/Admin/Users/New para a página de listar categorias e modifique os seguintes códigos: 10 | 11 | ```jsx 12 | ... 13 | import { Form } from 'react-bootstrap'; 14 | ... 15 | 16 | ... 17 | ``` 18 | 19 | 3. E deixe a
com a seguinte aparência: 20 | 21 | ```jsx 22 | ... 23 |
24 |
25 | Nome 26 | 27 | 28 | 29 |
30 | 31 | 32 |
33 |
34 | ... 35 | ``` -------------------------------------------------------------------------------- /public/snippets/6-criando_recuperacao_de_senha.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula vamos construir nosso formulário de recuperação de senha e conectar com nossa API! 4 | 5 | Antes de tudo, rode a nossa API com: 6 | 7 | ``` 8 | rails s -p 3000 9 | ``` 10 | 11 | # 2. Conteúdo 12 | 13 | 1. Agora, vamos criar nossa página de recuperação de senha. 14 | 15 | 2. Primeiro, vamos criar nosso componente components/shared/PasswordComponent, e dentro vamos criar o arquivo index.tsx com o seguinte conteúdo: 16 | 17 | ```jsx 18 | import React from 'react'; 19 | 20 | const PasswordComponent: React.FC = () => { 21 | return ( 22 |
23 | recover 24 |
25 | ) 26 | } 27 | 28 | export default PasswordComponent; 29 | ``` 30 | 31 | 3. Agora vamos centralizar nosso conteúdo com o BlueBackground: 32 | 33 | ```jsx 34 | import BlueBackground from '../shared/BlueBackground'; 35 | 36 | ... 37 | 38 | ... 39 | 40 | ... 41 | ``` 42 | 43 | 4. Vamos envolver o código tipando com o typescript: 44 | 45 | ```jsx 46 | ... 47 | const PasswordComponent: React.FC = ({children}) => { 48 | return ( 49 | <> 50 |

Recuperar senha

51 | 52 | 53 | {children} 54 | 55 | 56 | ) 57 | } 58 | ``` 59 | 60 | 5. Agora vamos centralizar nosso conteúdo: 61 | 62 | ```jsx 63 | import { Row, Col } from 'react-bootstrap'; 64 | ... 65 | 66 | 67 | 68 | 69 | ... 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | 77 | 7. Agora vamos criar nossa page pages/Auth/PasswordRecovery, e dentro crie o index.tsx com o seguinte conteúdo: 78 | 79 | ```jsx 80 | import React from 'react'; 81 | 82 | const PasswordRecovery: React.FC = () => { 83 | return ( 84 |
85 | form 86 |
87 | ) 88 | } 89 | 90 | export default PasswordRecovery; 91 | ``` 92 | 93 | 8. Vamos importar nosso componente PasswordComponent, envolvendo o conteúdo e adicionando o seguinte código: 94 | 95 | ```jsx 96 | ... 97 | import PasswordComponent from '../../../components/shared/PasswordComponent'; 98 | 99 | ... 100 | 101 | return ( 102 | 103 |
Digite o Email Cadastrado
104 |
105 | ) 106 | ... 107 | ``` 108 | 109 | 110 | 9. Agora vamos estilizar com o boostrap: 111 | 112 | ```jsx 113 | ... 114 | import React from 'react'; 115 | import { InputGroup, FormControl, Button } from 'react-bootstrap'; 116 | 117 | ... 118 | const PasswordRecovery: React.FC = () => { 119 | ... 120 |
121 | ... 122 | 123 | 124 | 125 | 126 | 127 |
128 | 129 | ... 130 | ``` 131 | 132 | 10. Agora vamos importar nosso MainComponent na page PasswordRecovery: 133 | 134 | ```jsx 135 | import MainComponent from '../../../components/shared/MainComponent'; 136 | 137 | ... 138 | 139 | 140 | ... 141 | 142 | 143 | ... 144 | ``` 145 | 146 | 13. No componente components/LoginForm/index.tsx, modifique e adicione os seguintes códigos: 147 | 148 | ```jsx 149 | import Link from 'next/link'; 150 | ... 151 | Esqueci minha senha
152 | ... 153 | ``` -------------------------------------------------------------------------------- /public/snippets/7-criando_modificacao_de_senha.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos conectar nosso componente de modificação de senha, à nossa API, após ter requisitado a mudança de senha por email. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie o componente pages/Auth/ChangePassword, e dentro crie o arquivo index.tsx com o seguinte formato: 8 | 9 | ```jsx 10 | import React from 'react'; 11 | 12 | const ChangePassword: React.FC = () => { 13 | return ( 14 |
15 | Troca de senha 16 |
17 | ) 18 | } 19 | 20 | export default ChangePassword; 21 | ``` 22 | 23 | 3. No nosso componente pages/Auth/ChangePassword, vamos começar a estilizá-lo. Insira o seguinte código: 24 | 25 | ```jsx 26 | import { InputGroup, FormControl, Button } from 'react-bootstrap'; 27 | 28 | ... 29 |
30 |
Criar nova senha
31 | 32 |
33 | 34 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 |
47 |
48 | ... 49 | ``` 50 | 51 | 4. Agora, vamos envolver nosso componente com o outro componente PasswordComponent: 52 | 53 | ```jsx 54 | import PasswordComponent from '../../../components/shared/PasswordComponent'; 55 | ... 56 | return ( 57 | 58 | 59 | ... 60 | 61 | 62 | ) 63 | ... 64 | ``` 65 | 66 | 5. Agora vamos importar nosso MainComponent por fora do conteúdo: 67 | 68 | ```jsx 69 | import MainComponent from '../../../components/shared/MainComponent'; 70 | ... 71 | 72 | ... 73 | 74 | 75 | ... 76 | ``` -------------------------------------------------------------------------------- /public/snippets/9-criando_footer_admin.md: -------------------------------------------------------------------------------- 1 | # 1. Introdução 2 | 3 | Nessa aula, vamos criar o footer do nosso painel do admin. 4 | 5 | # 2. Conteúdo 6 | 7 | 1. Crie o componente components/shared/AdminFooter e crie o arquivo index.tsx. 8 | 9 | ```jsx 10 | import React from 'react'; 11 | 12 | const AdminFooter: React.FC = () => { 13 | return ( 14 |
15 | footer 16 |
17 | ) 18 | } 19 | 20 | export default AdminFooter; 21 | ``` 22 | 23 | 2. Agora vamos colocar um pouco do bootstrap em nosso footer: 24 | 25 | ```jsx 26 | ... 27 | import { Col, Row, Container } from 'react-bootstrap'; 28 | ... 29 | 30 | 31 | 32 | 33 | Logo 34 | 35 | 36 | 37 | onebitcode.com • contato@onebitcode.com 38 | 39 | 40 | 41 | ... 42 | ``` 43 | 44 | 3. Agora vamos importar nosso componente Logo, no Footer: 45 | 46 | ```jsx 47 | ... 48 | import Logo from '../../Logo'; 49 | ... 50 | 51 | 52 | 53 | ... 54 | ``` 55 | 56 | 4. Vamos no components/shared/AdminComponent vamos importar nosso Footer no AdminRoute: 57 | 58 | ```jsx 59 | import AdminFooter from '../../Footer/AdminFooter'; 60 | ... 61 |
62 | { children } 63 |
64 | 65 | 66 | ``` -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /services/adminOrders.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Meta from '../dtos/Meta'; 3 | import OrdersList from '../dtos/OrdersList'; 4 | 5 | interface OrdersIndexData { 6 | orders: OrdersList[]; 7 | meta: Meta; 8 | } 9 | 10 | const AdminOrdersService = { 11 | index(url: string) { 12 | return api.get(url).then(resp => resp.data); 13 | } 14 | } 15 | 16 | export default AdminOrdersService; -------------------------------------------------------------------------------- /services/api.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios'; 2 | import Cookie from 'js-cookie'; 3 | import ApiData from '../dtos/ApiData'; 4 | import ApiResponseError from '../dtos/ApiResponseError'; 5 | 6 | // importação do router para que possamos realizar o redirect caso o usuário recebe a mensagem da api de que não acesso ao recurso que tentou acessar. 7 | import Router from 'next/router'; 8 | import { toast } from 'react-toastify'; 9 | 10 | const api = axios.create({ 11 | baseURL: 'http://localhost:3000' 12 | }); 13 | 14 | // adição da função para setar os headers de authẽnticação na api e nos cookies do browser, iremos utilizar ela no interceptor de request (tanto no fluxo normal quando no fluxo de erro). 15 | function setHeaders(res: AxiosResponse) { 16 | if(res.headers['access-token'] && res.headers['access-token'] !== '') { 17 | const apiData: ApiData = { 18 | 'access-token': res.headers['access-token'], 19 | client: res.headers.client, 20 | expiry: res.headers.expiry, 21 | 'token-type': res.headers['token-type'], 22 | uid: res.headers.uid 23 | }; 24 | 25 | api.defaults.headers = apiData; 26 | Cookie.set('@api-data', apiData); 27 | } 28 | } 29 | 30 | api.interceptors.response.use(res => { 31 | setHeaders(res); 32 | return res; 33 | } 34 | , err => { 35 | // caso um erro ocorra na response, um novo token é retornado, logo devemos atualizá-lo na api e nos cookies 36 | if (err.response) { 37 | setHeaders(err.response); 38 | 39 | const data = err.response.data; 40 | 41 | // aqui estamos tratando os erros no padrão que o rails no devolve, se existem algum array de erros, iremos extrair o nome do campo e as mensagens para que as mesmas possam ser exibidas na tela utilizando um toast 42 | if (data && data.errors && data.errors.fields) { 43 | const errors = data.errors as ApiResponseError; 44 | 45 | const fieldsName = Object.keys(errors.fields) 46 | 47 | fieldsName.forEach(error => { 48 | toast.error(error + ': ' + errors.fields[error].join(`, `)) 49 | }) 50 | 51 | console.log('errors', errors); 52 | } 53 | } 54 | 55 | // caso a response tenha um status de não autorizado ou acesso negado, o usuário será redirecionado para o login. 56 | if (err.response && ( 57 | err.response.status === 401 || 58 | err.response.status === 403 59 | )) { 60 | Router.push('/Auth/Login'); 61 | } 62 | 63 | throw err; 64 | }); 65 | 66 | api.interceptors.request.use(req => { 67 | req.headers = { ContentType: 'application/json' }; 68 | 69 | if ( 70 | req.url.includes('admin') || 71 | req.url.includes('storefront/v1/wish_items') || 72 | req.url.includes('auth/v1/user') || 73 | req.url.includes('storefront/v1/coupons') || 74 | req.url.includes('storefront/v1/checkouts') || 75 | req.url.includes('storefront/v1/orders') || 76 | req.url.includes('storefront/v1/games') 77 | ) { 78 | const apiDataCookie = Cookie.get('@api-data'); 79 | 80 | if (!apiDataCookie) { 81 | return req; 82 | } 83 | 84 | const apiData: ApiData = JSON.parse(apiDataCookie); 85 | req.headers = { ...apiData, ...req.headers }; 86 | } 87 | 88 | return req; 89 | }) 90 | 91 | export default api; -------------------------------------------------------------------------------- /services/categories.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Category from '../dtos/Category'; 3 | import Meta from '../dtos/Meta'; 4 | 5 | // criação da interface que será retornada pela listagem de categorias da api. 6 | // por padrão sempre será um array do recurso mas um objeto meta, contendo os dados para a páginação 7 | interface CategoryIndexData { 8 | categories: Category[]; 9 | meta: Meta; 10 | } 11 | 12 | 13 | const CategoriesService = { 14 | // função que irá realizar o fetch das categorias 15 | // recebemos a url do SWR e apenas retornamos os dados da reposta para ficar mais fácil a tratativa pelo componente de listagem 16 | index: (url: string) => { 17 | return api.get(url).then(response => response.data); 18 | }, 19 | 20 | // função para a crição de uma nova categoria 21 | create: (name: string) => { 22 | return api.post('/admin/v1/categories', { category: { name } }); 23 | }, 24 | 25 | // função para a atualização da categoria 26 | update: ({id, name}: Category) => { 27 | return api.put(`/admin/v1/categories/${id}`, { category: { name } }); 28 | }, 29 | 30 | // função para remoção de uma categoria 31 | delete: (id: number) => { 32 | return api.delete(`/admin/v1/categories/${id}`); 33 | } 34 | } 35 | 36 | export default CategoriesService; -------------------------------------------------------------------------------- /services/checkout.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Checkout from '../dtos/Checkout'; 3 | 4 | interface CheckoutResponseData { 5 | order: { 6 | id: number; 7 | } 8 | } 9 | 10 | const CheckoutService = { 11 | execute(checkout: Checkout) { 12 | return api.post('/storefront/v1/checkouts', checkout) 13 | .then(response => response.data.order); 14 | } 15 | } 16 | 17 | export default CheckoutService; -------------------------------------------------------------------------------- /services/coupons.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | import Coupon from '../dtos/Coupon'; 4 | import Meta from '../dtos/Meta'; 5 | 6 | interface CouponsIndexData { 7 | coupons: Coupon[], 8 | meta: Meta 9 | } 10 | 11 | const CouponsService = { 12 | index(url: string) { 13 | return api.get(url).then(response => response.data); 14 | }, 15 | 16 | create(coupon: Coupon) { 17 | return api.post('/admin/v1/coupons', { coupon: coupon }); 18 | }, 19 | 20 | update(coupon: Coupon) { 21 | return api.patch(`/admin/v1/coupons/${coupon.id}`, { coupon: coupon }); 22 | }, 23 | 24 | delete(id: number) { 25 | return api.delete(`/admin/v1/coupons/${id}`); 26 | } 27 | } 28 | 29 | export default CouponsService; -------------------------------------------------------------------------------- /services/dashboardSalesRange.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | interface SalesRangeItem { 4 | date: string; 5 | total_sold: number; 6 | } 7 | 8 | interface DashboardSalesRange { 9 | sales_ranges: SalesRangeItem[]; 10 | } 11 | 12 | const DashboardSalesRangeService = { 13 | index(url: string) { 14 | return api.get(url).then(resp => resp.data.sales_ranges); 15 | } 16 | } 17 | 18 | export default DashboardSalesRangeService; -------------------------------------------------------------------------------- /services/dashboardSumarry.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | interface DashboardSummary { 4 | summary: { 5 | orders: number; 6 | products: number; 7 | profit: number; 8 | users: number; 9 | } 10 | } 11 | 12 | const DashboardSummaryService = { 13 | index(url: string) { 14 | return api.get(url).then(resp => resp.data.summary); 15 | } 16 | } 17 | 18 | export default DashboardSummaryService; -------------------------------------------------------------------------------- /services/dashboardTopProducts.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | interface TopProduct { 4 | image: string; 5 | product: string; 6 | quantity: number; 7 | total_sold: number; 8 | } 9 | 10 | interface DashboardTopProduct { 11 | top_five_products: TopProduct[]; 12 | } 13 | 14 | const DashboardTopProductService = { 15 | index(url: string) { 16 | return api.get(url).then(resp => resp.data.top_five_products); 17 | } 18 | } 19 | 20 | export default DashboardTopProductService; -------------------------------------------------------------------------------- /services/games.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Game from '../dtos/Games'; 3 | 4 | interface GamesIndexData { 5 | games: Game[]; 6 | } 7 | 8 | const GamesService = { 9 | index(url: string) { 10 | return api.get(url).then(resp => resp.data.games); 11 | } 12 | } 13 | 14 | export default GamesService; -------------------------------------------------------------------------------- /services/home.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import HomeIndexData from '../dtos/HomeIndexData'; 3 | 4 | const HomeService = { 5 | index: (url: string) => { 6 | return api.get(url).then(response => response.data); 7 | } 8 | } 9 | 10 | export default HomeService; -------------------------------------------------------------------------------- /services/order.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Order from '../dtos/Order'; 3 | import OrdersList from '../dtos/OrdersList'; 4 | 5 | interface OrderShowData { 6 | order: Order; 7 | } 8 | 9 | interface OrderIndexData { 10 | orders: OrdersList[]; 11 | } 12 | 13 | const OrderService = { 14 | index(url: string) { 15 | return api.get(url).then(resp => resp.data.orders); 16 | }, 17 | 18 | show(url: string) { 19 | return api.get(url).then(resp => resp.data.order); 20 | } 21 | } 22 | 23 | export default OrderService; -------------------------------------------------------------------------------- /services/productShow.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import ProductShowData from '../dtos/ProductShowData'; 3 | 4 | const ProductShowService = { 5 | show: (url: string) => { 6 | return api.get(url).then(response => response.data.product); 7 | } 8 | } 9 | 10 | export default ProductShowService; -------------------------------------------------------------------------------- /services/products.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Product from '../dtos/Product'; 3 | import Meta from '../dtos/Meta'; 4 | 5 | interface ProductsIndexData { 6 | products: Product[]; 7 | meta: Meta; 8 | } 9 | 10 | const ProductsService = { 11 | index: (url: string) => { 12 | return api.get(url).then(response => response.data); 13 | }, 14 | 15 | create: (product: FormData) => { 16 | return api.post('/admin/v1/products', product); 17 | }, 18 | 19 | update: (product: FormData) => { 20 | return api.patch( 21 | `/admin/v1/products/${product.get('product[id]')}`, 22 | product 23 | ); 24 | }, 25 | 26 | delete: (id: number) => { 27 | return api.delete(`/admin/v1/products/${id}`); 28 | } 29 | } 30 | 31 | export default ProductsService; -------------------------------------------------------------------------------- /services/profile.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import User from '../dtos/User'; 3 | 4 | const ProfileService = { 5 | update(user: User) { 6 | return api.put('/auth/v1/user', user); 7 | } 8 | } 9 | 10 | export default ProfileService; 11 | -------------------------------------------------------------------------------- /services/search.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | import ProductSearch from '../dtos/ProductSearch'; 4 | import Meta from '../dtos/Meta'; 5 | 6 | interface SearchIndexData { 7 | products: ProductSearch[]; 8 | meta: Meta; 9 | } 10 | 11 | const SearchService = { 12 | execute: (url: string) => { 13 | return api.get(url).then(response => response.data); 14 | } 15 | } 16 | 17 | export default SearchService; 18 | -------------------------------------------------------------------------------- /services/systemRequirements.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | 3 | import SystemRequirement from '../dtos/SystemRequirement'; 4 | import Meta from '../dtos/Meta'; 5 | 6 | interface SystemRequirementIndexData { 7 | system_requirements: SystemRequirement[]; 8 | meta: Meta; 9 | }; 10 | 11 | const SystemRequirementsService = { 12 | index: (url: string) => { 13 | return api.get(url).then(response => response.data) 14 | }, 15 | 16 | create: (system_requirement: SystemRequirement) => { 17 | return api.post('/admin/v1/system_requirements', { system_requirement: system_requirement} ); 18 | }, 19 | 20 | update: (system_requirement: SystemRequirement) => { 21 | return api.patch(`/admin/v1/system_requirements/${system_requirement.id}`, { system_requirement: system_requirement }); 22 | }, 23 | 24 | delete: (id: number) => { 25 | return api.delete(`/admin/v1/system_requirements/${id}`); 26 | } 27 | 28 | 29 | } 30 | 31 | export default SystemRequirementsService; -------------------------------------------------------------------------------- /services/users.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import User from '../dtos/User'; 3 | import Meta from '../dtos/Meta'; 4 | 5 | interface SignInData { 6 | email: string; 7 | password: string; 8 | } 9 | 10 | interface SignUpData { 11 | name: string; 12 | email: string; 13 | password: string; 14 | password_confirmation: string; 15 | } 16 | 17 | interface ChangePasswordData { 18 | password: string; 19 | password_confirmation: string; 20 | reset_password_token: string | string[]; 21 | } 22 | 23 | interface SignInResponse { 24 | data: User 25 | } 26 | 27 | interface DefaultResponse { 28 | message: string; 29 | } 30 | 31 | interface IUsersIndexData { 32 | users: User[], 33 | meta: Meta 34 | } 35 | 36 | const UsersService = { 37 | signUp: ({ 38 | name, 39 | email, 40 | password, 41 | password_confirmation 42 | }: SignUpData) => 43 | api.post('/auth/v1/user', { 44 | name, 45 | email, 46 | password, 47 | password_confirmation 48 | }), 49 | 50 | signIn: ({ email, password }: SignInData) => 51 | api.post('auth/v1/user/sign_in', { 52 | email, 53 | password 54 | }), 55 | 56 | resetPassword: (email: string) => 57 | api.post('/auth/v1/user/password', { 58 | email, 59 | redirect_url: process.env.redirect_url 60 | }), 61 | 62 | changePassword: ({ 63 | password, 64 | password_confirmation, 65 | reset_password_token 66 | }: ChangePasswordData) => 67 | api.patch('/auth/v1/user/password', { 68 | password, 69 | password_confirmation, 70 | reset_password_token 71 | }), 72 | 73 | index(url: string) { 74 | return api.get(url).then(response => response.data); 75 | }, 76 | 77 | create(user: User) { 78 | return api.post('/admin/v1/users', { user: user }); 79 | }, 80 | 81 | update(user: User) { 82 | return api.put(`/admin/v1/users/${user.id}`, { user: user }); 83 | }, 84 | 85 | delete(id: number) { 86 | return api.delete(`/admin/v1/users/${id}`); 87 | } 88 | } 89 | 90 | export default UsersService; -------------------------------------------------------------------------------- /services/validateCoupon.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import Coupon from '../dtos/Coupon'; 3 | 4 | interface ValidateCouponData { 5 | coupon: Coupon; 6 | } 7 | 8 | const ValidateCouponService = { 9 | execute(code: string) { 10 | return api.post(`/storefront/v1/coupons/${code}/validations`) 11 | .then(response => response.data.coupon); 12 | } 13 | } 14 | 15 | export default ValidateCouponService; -------------------------------------------------------------------------------- /services/wishlist.ts: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import ProductSearch from '../dtos/ProductSearch'; 3 | 4 | interface WishlistIndexData { 5 | wish_items: ProductSearch[]; 6 | } 7 | 8 | const WishlistService = { 9 | index(url: string) { 10 | return api.get(url).then(response => response.data); 11 | }, 12 | 13 | add(productId: number) { 14 | return api.post('/storefront/v1/wish_items', { wish_item: { product_id: productId } }); 15 | }, 16 | 17 | remove(productId: number) { 18 | return api.delete(`/storefront/v1/wish_items/${productId}`); 19 | } 20 | } 21 | 22 | export default WishlistService; -------------------------------------------------------------------------------- /store/index.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { persistStore, persistReducer } from 'redux-persist'; 3 | import storage from 'redux-persist/lib/storage'; 4 | import rootReducer from './modules/rootReducer'; 5 | 6 | const persistConfig = { 7 | key: "root", 8 | storage 9 | }; 10 | 11 | const persistedReducer = persistReducer(persistConfig, rootReducer); 12 | 13 | const store = configureStore({ reducer: persistedReducer }) 14 | const persistor = persistStore(store); 15 | 16 | export { store, persistor }; -------------------------------------------------------------------------------- /store/modules/admin/category/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import Category from '../../../../dtos/Category'; 4 | 5 | const categorySlice = createSlice({ 6 | name: 'category', 7 | initialState: null, 8 | reducers: { 9 | setCategoryToEdit(state: Category, action: PayloadAction) { 10 | return state = action.payload; 11 | }, 12 | clearCategoryToEdit(state: Category) { 13 | return state = null; 14 | } 15 | } 16 | }) 17 | 18 | export const { setCategoryToEdit, clearCategoryToEdit } = categorySlice.actions; 19 | export default categorySlice.reducer; -------------------------------------------------------------------------------- /store/modules/admin/coupon/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import Coupon from '../../../../dtos/Coupon'; 4 | 5 | const couponReducer = createSlice({ 6 | name: 'coupon', 7 | initialState: null, 8 | reducers: { 9 | setCouponToEdit(state: Coupon, action: PayloadAction) { 10 | return state = action.payload; 11 | }, 12 | clearCouponToEdit(state: Coupon) { 13 | return state = null; 14 | } 15 | } 16 | }); 17 | 18 | export const { setCouponToEdit, clearCouponToEdit } = couponReducer.actions; 19 | export default couponReducer.reducer; -------------------------------------------------------------------------------- /store/modules/admin/dashboard/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import Dashboard from '../../../../dtos/Dashboard'; 4 | 5 | const dashboardSlice = createSlice({ 6 | name: 'dashboard', 7 | initialState: {} as Dashboard, 8 | reducers: { 9 | updateDates(state: Dashboard, action: PayloadAction) { 10 | return state = action.payload; 11 | } 12 | } 13 | }); 14 | 15 | export const { updateDates } = dashboardSlice.actions; 16 | export default dashboardSlice.reducer; -------------------------------------------------------------------------------- /store/modules/admin/product/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import Product from '../../../../dtos/Product'; 4 | 5 | const productSlice = createSlice({ 6 | name: 'product', 7 | initialState: null, 8 | reducers: { 9 | setProductToEdit(state: Product, action: PayloadAction) { 10 | return state = action.payload; 11 | }, 12 | clearProductToEdit(state: Product) { 13 | return state = null; 14 | } 15 | } 16 | }); 17 | 18 | export const { setProductToEdit, clearProductToEdit } = productSlice.actions; 19 | export default productSlice.reducer; -------------------------------------------------------------------------------- /store/modules/admin/shared/search/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | const searchSlice = createSlice({ 4 | name: 'search', 5 | initialState: '', 6 | reducers: { 7 | setSearch(state: string, action: PayloadAction) { 8 | return state = action.payload; 9 | }, 10 | clearSearch(state: string) { 11 | return state = ''; 12 | } 13 | } 14 | }) 15 | 16 | export const { setSearch, clearSearch } = searchSlice.actions; 17 | export default searchSlice.reducer; -------------------------------------------------------------------------------- /store/modules/admin/systemRequirement/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import SystemRequirement from '../../../../dtos/SystemRequirement'; 4 | 5 | const systemRequirementSlice = createSlice({ 6 | name: 'systemRequirement', 7 | initialState: null, 8 | reducers: { 9 | setSystemRequirementToEdit(state: SystemRequirement, action: PayloadAction) { 10 | return state = action.payload; 11 | }, 12 | clearSystemRequirementToEdit(state: SystemRequirement) { 13 | return state = null; 14 | } 15 | } 16 | }); 17 | 18 | export const { setSystemRequirementToEdit, clearSystemRequirementToEdit } = systemRequirementSlice.actions; 19 | export default systemRequirementSlice.reducer; -------------------------------------------------------------------------------- /store/modules/admin/user/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import User from '../../../../dtos/User'; 4 | 5 | const userReducer = createSlice({ 6 | name: 'user', 7 | initialState: null, 8 | reducers: { 9 | setUserToEdit(state: User, action: PayloadAction) { 10 | return state = action.payload; 11 | }, 12 | clearUserToEdit(state: User) { 13 | return state = null; 14 | } 15 | } 16 | }); 17 | 18 | export const { setUserToEdit, clearUserToEdit } = userReducer.actions; 19 | export default userReducer.reducer; -------------------------------------------------------------------------------- /store/modules/auth/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import User from '../../../dtos/User'; 4 | import ApiData from '../../../dtos/ApiData'; 5 | 6 | type State = { 7 | loggedUser: User; 8 | apiData: ApiData; 9 | } 10 | 11 | const authSlice = createSlice({ 12 | name: 'auth', 13 | initialState: { loggedUser: null, apiData: null } as State, 14 | reducers: { 15 | setLoggedUser(state, action: PayloadAction) { 16 | state.loggedUser = action.payload; 17 | }, 18 | clearLoggedUser(state) { 19 | state.loggedUser = null; 20 | }, 21 | setApiData(state, action: PayloadAction) { 22 | state.apiData = action.payload; 23 | }, 24 | clearApiData(state) { 25 | state.apiData = null; 26 | } 27 | } 28 | }) 29 | 30 | export const { setLoggedUser, clearLoggedUser, setApiData, clearApiData } = authSlice.actions; 31 | export default authSlice.reducer; -------------------------------------------------------------------------------- /store/modules/rootReducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import auth from './auth/reducer'; 4 | import category from './admin/category/reducer'; 5 | import product from './admin/product/reducer'; 6 | import systemRequirement from './admin/systemRequirement/reducer'; 7 | import coupon from './admin/coupon/reducer'; 8 | import user from './admin/user/reducer'; 9 | 10 | import search from './admin/shared/search/reducer'; 11 | 12 | import cartProducts from './storefront/cartProducts/reducer'; 13 | 14 | import dashboard from './admin/dashboard/reducer'; 15 | 16 | export default combineReducers({ 17 | auth, 18 | category, 19 | product, 20 | systemRequirement, 21 | coupon, 22 | user, 23 | search, 24 | cartProducts, 25 | dashboard 26 | }); -------------------------------------------------------------------------------- /store/modules/storefront/cartProducts/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import ProductShow from '../../../../dtos/ProductShow'; 3 | 4 | const cartReducer = createSlice({ 5 | name: 'cartProducts', 6 | initialState: [] as ProductShow[], 7 | reducers: { 8 | addCartProduct(state: ProductShow[], action: PayloadAction) { 9 | return [...state, action.payload]; 10 | }, 11 | removeCartProduct(state: ProductShow[], action: PayloadAction) { 12 | return [...state.filter((_, index) => index !== action.payload)]; 13 | }, 14 | clearCartProducts() { 15 | return [] as ProductShow[]; 16 | } 17 | } 18 | }); 19 | 20 | export const { addCartProduct, removeCartProduct, clearCartProducts } = cartReducer.actions; 21 | export default cartReducer.reducer; -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400&display=swap'); 2 | 3 | :root { 4 | --color-primary: #10163A; 5 | --color-secondary: #212744; 6 | --color-background: #262C49; 7 | --color-gray-light: #7DA1BC; 8 | --color-blue-light: #00CFFF; 9 | --color-white: #FFFFFF; 10 | --color-hover: #055bd8; 11 | --color-active: #71efff; 12 | } 13 | 14 | html, 15 | body { 16 | padding: 0; 17 | margin: 0; 18 | font-family: 'Raleway', sans-serif; 19 | background: var(--color-background); 20 | color: white; 21 | overflow-x: hidden; 22 | } 23 | 24 | body, .sticky-footer-wrapper { 25 | min-height:100vh; 26 | } 27 | 28 | a { 29 | color: inherit; 30 | text-decoration: none; 31 | transition: color 0.2s; 32 | } 33 | 34 | a:hover { 35 | text-decoration: none; 36 | color: var(--color-hover); 37 | } 38 | 39 | a:hover svg { 40 | transition: color 0.2s; 41 | color: var(--color-hover); 42 | } 43 | 44 | a:active, 45 | a:active svg { 46 | color: var(--color-active); 47 | } 48 | 49 | * { 50 | box-sizing: border-box; 51 | } 52 | 53 | input[type="date"]::-webkit-calendar-picker-indicator { 54 | filter: invert(0.7); 55 | transition: filter 0.2s; 56 | } 57 | 58 | input[type="date"]::-webkit-calendar-picker-indicator:hover { 59 | filter: invert(1); 60 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "include": [ 22 | "next-env.d.ts", 23 | "**/*.ts", 24 | "**/*.tsx" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /util/AggregateItemsService.ts: -------------------------------------------------------------------------------- 1 | import ProductShow from '../dtos/ProductShow'; 2 | import CheckoutItem from '../dtos/CheckoutItem'; 3 | 4 | const AggregateItemsService = { 5 | execute(cartProducts: ProductShow[]): CheckoutItem[] { 6 | const checkoutItems = cartProducts.map( 7 | checkoutItem => checkoutItem.id 8 | ).filter( 9 | (checkoutItem, index, self) => 10 | self.indexOf(checkoutItem) === index 11 | ); 12 | 13 | const items = checkoutItems.map( 14 | product_id => ({ 15 | product_id, 16 | quantity: cartProducts 17 | .filter(product => product.id === product_id).length 18 | }) 19 | ); 20 | 21 | return items; 22 | } 23 | } 24 | 25 | export default AggregateItemsService; -------------------------------------------------------------------------------- /util/JunoService.ts: -------------------------------------------------------------------------------- 1 | interface JunoProps { 2 | cardNumber: string; 3 | holderName: string; 4 | securityCode: string; 5 | expirationMonth: string; 6 | expirationYear: string; 7 | } 8 | 9 | const JunoService = { 10 | execute(card: JunoProps): Promise { 11 | let checkout = new DirectCheckout('A30F934C2A5A050EF325382103132761FE62225D97C615A468CEE4951EAD0B99', false); 12 | let cardData = card; 13 | 14 | return new Promise((resolve, reject) => { 15 | checkout.getCardHash(cardData, 16 | (data) => resolve(data), 17 | (error) => reject(error) 18 | ); 19 | }); 20 | } 21 | } 22 | 23 | export default JunoService; -------------------------------------------------------------------------------- /util/LoggedService.ts: -------------------------------------------------------------------------------- 1 | import Cookie from 'js-cookie'; 2 | 3 | const LoggedService = { 4 | execute(): boolean { 5 | const apiData = Cookie.get('@api-data'); 6 | return !!apiData; 7 | } 8 | } 9 | 10 | export default LoggedService; -------------------------------------------------------------------------------- /util/MonthsService.ts: -------------------------------------------------------------------------------- 1 | import { ptBR } from 'date-fns/locale'; 2 | 3 | const MonthsService = { 4 | execute(): string[] { 5 | const months = []; 6 | for (var i = 0; i < 12; i++) { 7 | const month = ptBR.localize.month(i); 8 | months.push(month[0].toUpperCase() + month.slice(1)); 9 | } 10 | 11 | return months; 12 | } 13 | } 14 | 15 | export default MonthsService; -------------------------------------------------------------------------------- /util/OrderStatusService.ts: -------------------------------------------------------------------------------- 1 | const OrderStatusService = { 2 | execute(status: string): string { 3 | switch(status) { 4 | case 'processing_error': 5 | return 'Erro no processamento'; 6 | case 'waiting_payment': 7 | return 'Aguardando o pagamento'; 8 | case 'payment_accepted': 9 | return 'Pagamento aceito'; 10 | case 'payment_denied': 11 | return 'Pagamento negado'; 12 | case 'finished': 13 | return 'Finalizado'; 14 | default: 15 | return 'Pedido em processamento'; 16 | } 17 | } 18 | } 19 | 20 | export default OrderStatusService; -------------------------------------------------------------------------------- /util/PaginationService.ts: -------------------------------------------------------------------------------- 1 | const PaginationService = { 2 | execute(total_pages: number, current_page: number): Array { 3 | let arr: Array = []; 4 | 5 | arr.push('1'); 6 | 7 | // se o total de páginas for maior que 5, ele irá realizar a montagem do array de páginação de acordo com a página atual 8 | // se for menor que 5 páginas, todas serão retornadas no array de páginação 9 | if (total_pages > 5) { 10 | // se página atual for menor ou igual a 3, ele irá retornar as três primeiras paǵinas o simbolismo de mais páginas e a última página 11 | if (current_page <= 3) { 12 | arr.push('2') 13 | arr.push('3') 14 | // utilizado para simbolizar que existem páginas entre a listagem e o final 15 | arr.push('...') 16 | 17 | arr.push(total_pages.toString()); 18 | } else { 19 | // se página atual for maior que 3, ele irá retornar o array de páginação de acordo com a posição da página, com simbolimo e mais páginas antes ou depois do conjunto de páginas exibidos 20 | arr.push('...') 21 | arr.push((current_page - 1).toString()) 22 | arr.push(current_page.toString()) 23 | 24 | if (current_page + 1 < total_pages ) { 25 | arr.push((current_page + 1).toString()) 26 | } 27 | 28 | if (current_page + 2 < total_pages) { 29 | // utilizado para simbolizar que existem páginas entre a listagem e o inicial 30 | arr.push('...') 31 | } 32 | 33 | if (current_page < total_pages) { 34 | arr.push(total_pages.toString()); 35 | } 36 | } 37 | } else { 38 | for(let i = 2; i <= total_pages; i++) { 39 | arr.push(i.toString()); 40 | } 41 | } 42 | 43 | return arr; 44 | } 45 | } 46 | 47 | export default PaginationService -------------------------------------------------------------------------------- /util/ProductSearchService.ts: -------------------------------------------------------------------------------- 1 | interface ProductSearchServiceParams { 2 | search: string | string[]; 3 | category?: string | string[]; 4 | price?: string | string[]; 5 | page?: string | string[]; 6 | order?: string | string[]; 7 | direction?: string | string[]; 8 | } 9 | 10 | const ProductSearchService = { 11 | execute({ 12 | search, 13 | category, 14 | price, 15 | page, 16 | order = 'price', 17 | direction = 'asc' 18 | }: ProductSearchServiceParams): string { 19 | let returnStr = `?length=12&order[${order}]=${direction}`; 20 | 21 | if (search) { 22 | returnStr += `&search=${search}`; 23 | } 24 | 25 | if (page) { 26 | returnStr += `&page=${page}`; 27 | } 28 | 29 | if (category) { 30 | returnStr += `&category_ids[]=${category}`; 31 | } 32 | 33 | if (price) { 34 | let prices = price.toString().split('-'); 35 | returnStr += `&price[min]=${prices[0]}`; 36 | 37 | if (prices.length === 2) { 38 | returnStr += `&price[max]=${prices[1]}`; 39 | } 40 | } 41 | 42 | return returnStr; 43 | } 44 | } 45 | 46 | export default ProductSearchService; -------------------------------------------------------------------------------- /util/SignOutService.ts: -------------------------------------------------------------------------------- 1 | import Cookie from 'js-cookie'; 2 | 3 | const SignOutService = { 4 | execute(): void { 5 | Cookie.remove('@api-data'); 6 | } 7 | } 8 | 9 | export default SignOutService; -------------------------------------------------------------------------------- /util/UrlService.ts: -------------------------------------------------------------------------------- 1 | interface UrlServiceRequest { 2 | page: string | string[]; 3 | search?: string; 4 | } 5 | 6 | const UrlService = { 7 | execute({ page, search }: UrlServiceRequest): string { 8 | return `${!!search ? `?search[name]=${search}` : ''}` + 9 | `${!!search ? '&' : '?'}page=${page}`; 10 | } 11 | } 12 | 13 | export default UrlService; --------------------------------------------------------------------------------