Last active
July 15, 2022 15:20
-
-
Save caiquecastro/c656c0ff06a10bd54854c36b544096e9 to your computer and use it in GitHub Desktop.
Venturus 4Tech
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios from "axios"; | |
import ICategory from "../models/ICategory"; | |
import { IExpenses, SaveExpenseDto } from "../models/IExpense"; | |
export const api = axios.create({ | |
baseURL: "http://localhost:3333", | |
}); | |
export const getExpenses = async () => { | |
const response = await api.get<IExpenses[]>("/expenses"); | |
return response.data; | |
}; | |
export const getCategories = async () => { | |
const response = await api.get<ICategory[]>("/categories"); | |
return response.data; | |
}; | |
export const saveExpense = async (expense: SaveExpenseDto) => { | |
if (expense.id) { | |
const response = await api.put(`/expenses/${expense.id}`, expense); | |
return response.data; | |
} | |
const response = await api.post("/expenses", expense); | |
return response.data; | |
}; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { NextPage } from "next"; | |
import { useEffect, useState } from "react"; | |
import { | |
Table, | |
Thead, | |
Tbody, | |
Tr, | |
Th, | |
Td, | |
TableContainer, | |
Box, | |
Button, | |
Heading, | |
IconButton, | |
} from "@chakra-ui/react"; | |
import { api } from "../services/api"; | |
import { IExpenses } from "../models/IExpense"; | |
import { EditIcon } from "@chakra-ui/icons"; | |
interface Props { | |
expenses: IExpenses[]; | |
onAddExpense: () => void; | |
onEditExpense: (expense: IExpenses) => void; | |
} | |
const FinancesTable = ({ expenses, onAddExpense, onEditExpense }: Props) => { | |
const toBRL = (value: number) => | |
value.toLocaleString("pt-BR", { | |
currency: "BRL", | |
style: "currency", | |
}); | |
// let total = 0; | |
// for (let i = 0; i < expenses.length; i++) { | |
// total += expenses[i].value; | |
// } | |
const total = expenses.reduce((acc, cur) => { | |
return acc + cur.value; | |
}, 0); | |
return ( | |
<> | |
<Box | |
flexDirection="row" | |
display="flex" | |
w="80vw" | |
justifyContent="space-between" | |
> | |
<Heading size="lg"> | |
Total: {toBRL(total)} | |
</Heading> | |
<Button | |
bg="green.400" | |
color="white" | |
_hover={{ bg: "green.300" }} | |
_active={{ bg: "green.600" }} | |
onClick={() => onAddExpense()} | |
> | |
Adicionar despesa | |
</Button> | |
</Box> | |
<Box | |
mt={5} | |
border="1px solid" | |
borderColor="gray.100" | |
borderWidth={2} | |
borderRadius="md" | |
w="80vw" | |
> | |
<TableContainer> | |
<Table variant="simple"> | |
<Thead> | |
<Tr> | |
<Th isNumeric>ID</Th> | |
<Th>DATA</Th> | |
<Th>DESCRIÇÃO</Th> | |
<Th>CATEGORIA</Th> | |
<Th>VALOR</Th> | |
<Th>AÇÕES</Th> | |
</Tr> | |
</Thead> | |
<Tbody> | |
{expenses.map((expense) => ( | |
<Tr key={expense.id}> | |
<Td isNumeric>{expense.id}</Td> | |
<Td>{new Date(expense.date).toLocaleDateString("pt-BR")}</Td> | |
<Td>{expense.description}</Td> | |
<Td>{expense.category}</Td> | |
<Td>{toBRL(expense.value)}</Td> | |
<Td> | |
<IconButton | |
aria-label="Editar" | |
icon={<EditIcon />} | |
color="yellow.500" | |
variant="ghost" | |
onClick={() => { | |
onEditExpense(expense); | |
}} | |
/> | |
</Td> | |
</Tr> | |
))} | |
</Tbody> | |
</Table> | |
</TableContainer> | |
</Box> | |
</> | |
); | |
}; | |
export default FinancesTable; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default interface ICategory { | |
id: number; | |
name: string; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface IExpenses { | |
id: number; | |
date: number; | |
description: string; | |
category: string; | |
value: number; | |
} | |
export interface SaveExpenseDto extends Omit<IExpenses, 'id'> { | |
id?: number; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { NextPage } from "next"; | |
import Head from "next/head"; | |
import styles from "../styles/Home.module.css"; | |
import FinancesTable from "../components/FinancesTable"; | |
import { Heading, useDisclosure } from "@chakra-ui/react"; | |
import NewExpenseModal from "../components/NewExpenseModal"; | |
import { useEffect, useState } from "react"; | |
import { IExpenses } from "../models/IExpense"; | |
import { api, getExpenses } from "../services/api"; | |
const Home: NextPage = () => { | |
const { isOpen, onOpen, onClose } = useDisclosure(); | |
const [expenses, setExpenses] = useState<IExpenses[]>([]); | |
const [expenseToEdit, setExpenseToEdit] = useState<IExpenses>(); | |
const fetchExpenses = () => | |
getExpenses().then(expenseList => setExpenses(expenseList)); | |
useEffect(() => { | |
fetchExpenses(); | |
}, []); | |
return ( | |
<div className={styles.container}> | |
<Head> | |
<title>My finances</title> | |
<meta name="description" content="Vnt 4tech 2022" /> | |
<link rel="icon" href="/favicon.ico" /> | |
</Head> | |
<main className={styles.main}> | |
<Heading as="h2" mb="100px"> | |
My Finances | |
</Heading> | |
<FinancesTable | |
expenses={expenses} | |
onAddExpense={() => onOpen()} | |
onEditExpense={(expense) => { | |
setExpenseToEdit(expense); | |
onOpen(); | |
}} | |
/> | |
<NewExpenseModal | |
isOpen={isOpen} | |
expense={expenseToEdit} | |
onSave={() => { | |
fetchExpenses(); | |
onClose(); | |
setExpenseToEdit(undefined); | |
}} | |
onClose={() => { | |
onClose(); | |
setExpenseToEdit(undefined); | |
}} | |
/> | |
</main> | |
<footer className={styles.footer}> | |
<a | |
href="https://www.venturus.org.br/" | |
target="_blank" | |
rel="noopener noreferrer" | |
> | |
Powered by Venturus | |
</a> | |
</footer> | |
</div> | |
); | |
}; | |
export default Home; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Button, FormControl, FormLabel, Grid, GridItem, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, NumberDecrementStepper, NumberIncrementStepper, NumberInput, NumberInputField, NumberInputStepper, Select, useToast } from "@chakra-ui/react"; | |
import { useEffect, useState } from "react"; | |
import { IExpenses } from "../models/IExpense"; | |
import { getCategories, saveExpense } from "../services/api"; | |
interface Props { | |
isOpen: boolean; | |
expense?: IExpenses; | |
onSave: () => void; | |
onClose: () => void; | |
} | |
function NewExpenseModal({ isOpen, expense, onSave, onClose }: Props) { | |
const toast = useToast(); | |
const [description, setDescription] = useState(""); | |
const [value, setValue] = useState(1); | |
const [category, setCategory] = useState<string>(); | |
const [isLoading, setLoading] = useState(false); | |
const [categories, setCategories] = useState<string[]>([]); | |
useEffect(() => { | |
setDescription(expense?.description ?? ""); | |
setValue(expense?.value ?? 1); | |
setCategory(expense?.category); | |
}, [expense]); | |
useEffect(() => { | |
getCategories() | |
.then((categories) => setCategories(categories.map(category => category.name))); | |
}, []); | |
const handleAddExpense = async () => { | |
if (!category) { | |
return; | |
} | |
const expense = { | |
description: description, | |
value, | |
category, | |
date: Date.now(), | |
}; | |
setLoading(true); | |
await saveExpense(expense); | |
setLoading(false); | |
toast({ | |
title: "Despesa salva", | |
description: "Despesa foi salva com sucesso", | |
status: "success", | |
position: "top-right", | |
}); | |
onSave(); | |
}; | |
return ( | |
<Modal isOpen={isOpen} onClose={onClose}> | |
<ModalOverlay /> | |
<ModalContent> | |
<ModalHeader>Adicionar Despesa</ModalHeader> | |
<ModalCloseButton /> | |
<ModalBody> | |
<Grid | |
templateColumns={"repeat(2, 1fr)"} | |
templateRows={"repeat(2, 1fr)"} | |
gap={4} | |
> | |
<GridItem colSpan={2}> | |
<FormControl> | |
<FormLabel>Descrição</FormLabel> | |
<Input | |
placeholder="Descrição" | |
value={description} | |
onChange={(e) => setDescription(e.target.value)} | |
/> | |
</FormControl> | |
</GridItem> | |
<GridItem colSpan={1}> | |
<FormControl> | |
<FormLabel htmlFor="value">Valor</FormLabel> | |
<NumberInput min={1} value={value} onChange={(_, value) => setValue(value)}> | |
<NumberInputField id="value" /> | |
<NumberInputStepper> | |
<NumberIncrementStepper /> | |
<NumberDecrementStepper /> | |
</NumberInputStepper> | |
</NumberInput> | |
</FormControl> | |
</GridItem> | |
<GridItem colSpan={1}> | |
<FormControl> | |
<FormLabel htmlFor="category">Categoria</FormLabel> | |
<Select | |
id="category" | |
placeholder="Categoria" | |
value={category} | |
onChange={(e) => setCategory(e.target.value)} | |
> | |
{categories.map(category => ( | |
<option key={category} value={category}> | |
{category} | |
</option> | |
))} | |
</Select> | |
</FormControl> | |
</GridItem> | |
</Grid> | |
</ModalBody> | |
<ModalFooter> | |
<Button onClick={() => onClose()} mr={3}> | |
Cancelar | |
</Button> | |
<Button | |
colorScheme="green" | |
onClick={handleAddExpense} | |
disabled={isLoading} | |
> | |
{expense ? "Editar" : "Adicionar"} | |
</Button> | |
</ModalFooter> | |
</ModalContent> | |
</Modal> | |
); | |
} | |
export default NewExpenseModal; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment