diff --git a/frontend/index.html b/frontend/index.html index f82f918..cef7700 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,6 +4,9 @@ + + + fincheck diff --git a/frontend/src/app/contexts/AuthContext.tsx b/frontend/src/app/contexts/AuthContext.tsx index d43a4c0..23f4561 100644 --- a/frontend/src/app/contexts/AuthContext.tsx +++ b/frontend/src/app/contexts/AuthContext.tsx @@ -4,9 +4,11 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import { usersService } from "../services/usersSerice"; import { LaunchScreen } from "../../view/components/LaunchScreen"; import toast from "react-hot-toast"; +import { User } from "../entities/User"; interface AuthContextValue { signedIn: boolean; + user: User | undefined; signin(accessToken: string): void; signout(): void; } @@ -22,7 +24,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const queryClient = useQueryClient(); - const { isError, isFetching, isSuccess } = useQuery({ + const { isError, isFetching, isSuccess, data } = useQuery({ queryKey: ['users', 'me'], queryFn: () => usersService.me(), enabled: signedIn, @@ -51,7 +53,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { return ( diff --git a/frontend/src/app/entities/User.ts b/frontend/src/app/entities/User.ts new file mode 100644 index 0000000..c7dc357 --- /dev/null +++ b/frontend/src/app/entities/User.ts @@ -0,0 +1,4 @@ +export interface User { + name: string; + email: string; +} diff --git a/frontend/src/app/hooks/useBankAccount.ts b/frontend/src/app/hooks/useBankAccounts.ts similarity index 89% rename from frontend/src/app/hooks/useBankAccount.ts rename to frontend/src/app/hooks/useBankAccounts.ts index 3204afe..aadcc74 100644 --- a/frontend/src/app/hooks/useBankAccount.ts +++ b/frontend/src/app/hooks/useBankAccounts.ts @@ -1,10 +1,11 @@ import { useQuery } from "@tanstack/react-query"; -import { bankAccountsService } from "../services/bankAccountsService"; +import { bankAccountsService } from "../services/bankAccountsService/delete"; export function useBankAccounts() { const { data, isFetching } = useQuery({ queryKey: ['bankAccounts'], queryFn: bankAccountsService.getAll, + staleTime: Infinity, }); return { accounts: data ?? [], isFetching }; diff --git a/frontend/src/app/services/bankAccountsService/delete.ts b/frontend/src/app/services/bankAccountsService/delete.ts index 5e3b856..4b9802d 100644 --- a/frontend/src/app/services/bankAccountsService/delete.ts +++ b/frontend/src/app/services/bankAccountsService/delete.ts @@ -1,15 +1,11 @@ -import { httpClient } from '../httpClient' +import { create } from "./create"; +import { remove } from "./remove"; +import { getAll } from "./getAll"; +import { update } from "./update"; -export interface DeleteBankAccountParams { - id: string; - name: string; - initialBalance: number; - color: string; - type: 'CHECKING' | 'INVESTMENT' | 'CASH'; -} - -export async function remove(bankAccountId: string) { - const { data } = await httpClient.delete(`/bank-accounts/${bankAccountId}`); - - return data; -} +export const bankAccountsService = { + create, + getAll, + update, + remove, +}; diff --git a/frontend/src/app/services/bankAccountsService/index.ts b/frontend/src/app/services/bankAccountsService/index.ts deleted file mode 100644 index 02c4163..0000000 --- a/frontend/src/app/services/bankAccountsService/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { create } from "./create"; -import { remove } from "./delete"; -import { getAll } from "./getAll"; -import { update } from "./update"; - -export const bankAccountsService = { - create, - getAll, - update, - remove, -}; diff --git a/frontend/src/app/services/bankAccountsService/remove.ts b/frontend/src/app/services/bankAccountsService/remove.ts new file mode 100644 index 0000000..8ddbe31 --- /dev/null +++ b/frontend/src/app/services/bankAccountsService/remove.ts @@ -0,0 +1,7 @@ +import { httpClient } from '../httpClient' + +export async function remove(bankAccountId: string) { + const { data } = await httpClient.delete(`/bank-accounts/${bankAccountId}`); + + return data; +} diff --git a/frontend/src/app/services/transactionsService/delete.ts b/frontend/src/app/services/transactionsService/delete.ts new file mode 100644 index 0000000..45a9658 --- /dev/null +++ b/frontend/src/app/services/transactionsService/delete.ts @@ -0,0 +1,7 @@ +import { httpClient } from '../httpClient' + +export async function remove(transactionId: string) { + const { data } = await httpClient.delete(`/transactions/${transactionId}`); + + return data; +} diff --git a/frontend/src/app/services/transactionsService/index.ts b/frontend/src/app/services/transactionsService/index.ts index a421c2f..6b4eca4 100644 --- a/frontend/src/app/services/transactionsService/index.ts +++ b/frontend/src/app/services/transactionsService/index.ts @@ -1,7 +1,11 @@ import { create } from "./create"; +import { remove } from "./delete"; import { getAll } from "./getAll"; +import { update } from "./update"; export const transactionsService = { create, getAll, + update, + remove }; diff --git a/frontend/src/app/services/transactionsService/update.ts b/frontend/src/app/services/transactionsService/update.ts new file mode 100644 index 0000000..0a1d7ba --- /dev/null +++ b/frontend/src/app/services/transactionsService/update.ts @@ -0,0 +1,20 @@ +import { httpClient } from '../httpClient' + +export interface UpdateTransactionParams { + id: string; + bankAccountId: string; + categoryId: string; + name: string; + value: number; + date: string; + type: "INCOME" | "EXPENSE"; +} + +export async function update({ + id, + ...params +}: UpdateTransactionParams) { + const { data } = await httpClient.put(`/transactions/${id}`, params); + + return data; +} diff --git a/frontend/src/app/services/usersSerice/me.ts b/frontend/src/app/services/usersSerice/me.ts index 2d55e94..8920019 100644 --- a/frontend/src/app/services/usersSerice/me.ts +++ b/frontend/src/app/services/usersSerice/me.ts @@ -1,12 +1,10 @@ +import { User } from "../../entities/User"; import { httpClient } from "../httpClient"; -interface meResonse { - name: string; - email: string; -} +type MeResponse = User; export async function me() { - const { data } = await httpClient.get('/users/me'); + const { data } = await httpClient.get('/users/me'); return data; } diff --git a/frontend/src/view/components/UserMenu.tsx b/frontend/src/view/components/UserMenu.tsx index 10d9dd3..4b0d6b5 100644 --- a/frontend/src/view/components/UserMenu.tsx +++ b/frontend/src/view/components/UserMenu.tsx @@ -3,13 +3,13 @@ import { DropdownMenu } from "./DropdownMenu"; import { useAuth } from "../../app/hooks/useAuth"; export function UserMenu() { - const { signout } = useAuth(); + const { signout, user } = useAuth(); return (
- MG + {user?.name.slice(0, 2).toUpperCase()}
diff --git a/frontend/src/view/pages/Dashboard/components/Accounts/useAccountsController.ts b/frontend/src/view/pages/Dashboard/components/Accounts/useAccountsController.ts index b1c5405..709fa00 100644 --- a/frontend/src/view/pages/Dashboard/components/Accounts/useAccountsController.ts +++ b/frontend/src/view/pages/Dashboard/components/Accounts/useAccountsController.ts @@ -1,7 +1,7 @@ import { useMemo, useState } from "react"; import { useWindowWidth } from "../../../../../app/hooks/useWindowWidth"; import { useDashboard } from "../DashboardContext/useDashboard"; -import { useBankAccounts } from "../../../../../app/hooks/useBankAccount"; +import { useBankAccounts } from "../../../../../app/hooks/useBankAccounts"; export function UseAccountsController() { const windowWidth = useWindowWidth(); diff --git a/frontend/src/view/pages/Dashboard/components/Transactions/FiltersModal/useFiltersModalController.ts b/frontend/src/view/pages/Dashboard/components/Transactions/FiltersModal/useFiltersModalController.ts index aa20a65..1566c5b 100644 --- a/frontend/src/view/pages/Dashboard/components/Transactions/FiltersModal/useFiltersModalController.ts +++ b/frontend/src/view/pages/Dashboard/components/Transactions/FiltersModal/useFiltersModalController.ts @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useBankAccounts } from "../../../../../../app/hooks/useBankAccount"; +import { useBankAccounts } from "../../../../../../app/hooks/useBankAccounts"; export function useFiltersModalController() { const [selectedBankAccountId, setSelectedBankAccountId] = useState(undefined); diff --git a/frontend/src/view/pages/Dashboard/modals/EditAccountModal/useEditAccountModalController.ts b/frontend/src/view/pages/Dashboard/modals/EditAccountModal/useEditAccountModalController.ts index 7db70e8..a7164e7 100644 --- a/frontend/src/view/pages/Dashboard/modals/EditAccountModal/useEditAccountModalController.ts +++ b/frontend/src/view/pages/Dashboard/modals/EditAccountModal/useEditAccountModalController.ts @@ -3,7 +3,7 @@ import { useDashboard } from "../../components/DashboardContext/useDashboard"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { bankAccountsService } from "../../../../../app/services/bankAccountsService"; +import { bankAccountsService } from "../../../../../app/services/bankAccountsService/delete"; import { currencyStringToNumber } from "../../../../../app/utils/currencyStringToNumber"; import toast from "react-hot-toast"; import { useState } from "react"; @@ -56,7 +56,6 @@ export function useEditAccountModalController() { mutateAsync: removeAccount, } = useMutation(bankAccountsService.remove); - const handleSubmit = hookFormSubmit(async (data) => { try { await updateAccount({ diff --git a/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/index.tsx b/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/index.tsx index d9a7245..36a171d 100644 --- a/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/index.tsx +++ b/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/index.tsx @@ -7,6 +7,8 @@ import { Modal } from "../../../../components/Modal"; import { Select } from "../../../../components/Select"; import { useEditTransactionModalController } from "./useEditTransactionModalController"; import { Transaction } from "../../../../../app/entities/Transaction"; +import { ConfirmDeleteModal } from "../../../../components/icons/ConfirmDeleteModal"; +import { TrashIcon } from "../../../../components/icons/TrashIcon"; interface EditTransactionModalProps { transaction: Transaction | null; @@ -23,15 +25,36 @@ export function EditTransactionModal({ transaction, open, onClose }: EditTransac accounts, categories, isLoading, - } = useEditTransactionModalController(transaction); + isDeleteModalOpen, + isLoadingDelete, + handleDeleteTransaction, + handleCloseDeleteModal, + handleOpenDeleteModal + } = useEditTransactionModalController(transaction, onClose); const isExpense = transaction?.type === 'EXPENSE'; + if (isDeleteModalOpen) { + return ( + + ); + } + return ( + + + )} >
diff --git a/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/useEditTransactionModalController.ts b/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/useEditTransactionModalController.ts index d427fd6..d668914 100644 --- a/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/useEditTransactionModalController.ts +++ b/frontend/src/view/pages/Dashboard/modals/EditTransactionModal/useEditTransactionModalController.ts @@ -1,10 +1,14 @@ import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; -import { useBankAccounts } from "../../../../../app/hooks/useBankAccount"; +import { useBankAccounts } from "../../../../../app/hooks/useBankAccounts"; import { useCategories } from "../../../../../app/hooks/useCategories"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { Transaction } from "../../../../../app/entities/Transaction"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { transactionsService } from "../../../../../app/services/transactionsService"; +import { currencyStringToNumber } from "../../../../../app/utils/currencyStringToNumber"; +import toast from "react-hot-toast"; const schema = z.object({ value: z.union([ @@ -21,6 +25,7 @@ type FormData = z.infer; export function useEditTransactionModalController( transaction: Transaction | null, + onClose: () => void, ) { const { @@ -41,15 +46,79 @@ export function useEditTransactionModalController( const { accounts } = useBankAccounts(); const { categories: categoriesList } = useCategories(); + const queryClient = useQueryClient(); + const { + isLoading, + mutateAsync: updateTransaction + } = useMutation(transactionsService.update); + const { + isLoading: isLoadingDelete, + mutateAsync: removeTransaction, + } = useMutation(transactionsService.remove); + + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const handleSubmit = hookFormSubmit(async data => { - console.log(data); + try { + await updateTransaction({ + ...data, + id: transaction!.id, + type: transaction!.type, + value: currencyStringToNumber(data.value), + date: data.date.toISOString(), + }); + + queryClient.invalidateQueries({ queryKey: ['transactions'] }) + queryClient.invalidateQueries({ queryKey: ['bankAccounts'] }) + toast.success( + transaction?.type === "EXPENSE" + ? "Despesa editada com sucesso!" + : "Receita editada com sucesso!" + ); + onClose(); + } catch { + toast.error( + transaction?.type === "EXPENSE" + ? "Erro ao salvar a despesa!" + : "Erro ao salvar a receita!" + ); + } }); const categories = useMemo(() => { return categoriesList.filter(category => category.type === transaction?.type); }, [categoriesList, transaction]); + async function handleDeleteTransaction() { + try { + await removeTransaction(transaction!.id); + + queryClient.invalidateQueries({ queryKey: ['transactions'] }); + queryClient.invalidateQueries({ queryKey: ['bankAccounts'] }); + toast.success( + transaction?.type === "EXPENSE" + ? "A despesa foi deletade com sucesso!" + : "A receita foi deletade com sucesso!" + ); + onClose(); + } catch { + toast.error( + transaction?.type === "EXPENSE" + ? "Erro ao deletar a despesa!" + : "Erro ao deletar a receita!" + ); + } + } + + function handleOpenDeleteModal() { + setIsDeleteModalOpen(true); + } + + function handleCloseDeleteModal() { + setIsDeleteModalOpen(false); + } + + return { register, control, @@ -57,6 +126,11 @@ export function useEditTransactionModalController( handleSubmit, accounts, categories, - isLoading: false, + isLoading, + isDeleteModalOpen, + isLoadingDelete, + handleDeleteTransaction, + handleCloseDeleteModal, + handleOpenDeleteModal }; } diff --git a/frontend/src/view/pages/Dashboard/modals/NewAccountModal/useNewAccountModalController.ts b/frontend/src/view/pages/Dashboard/modals/NewAccountModal/useNewAccountModalController.ts index 7e489a3..ad2c0bc 100644 --- a/frontend/src/view/pages/Dashboard/modals/NewAccountModal/useNewAccountModalController.ts +++ b/frontend/src/view/pages/Dashboard/modals/NewAccountModal/useNewAccountModalController.ts @@ -3,7 +3,7 @@ import { useDashboard } from '../../components/DashboardContext/useDashboard' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useMutation, useQueryClient } from '@tanstack/react-query' -import { bankAccountsService } from '../../../../../app/services/bankAccountsService' +import { bankAccountsService } from '../../../../../app/services/bankAccountsService/delete' import toast from 'react-hot-toast' import { currencyStringToNumber } from '../../../../../app/utils/currencyStringToNumber' diff --git a/frontend/src/view/pages/Dashboard/modals/NewTransactionModal/useNewTransactionModalController.ts b/frontend/src/view/pages/Dashboard/modals/NewTransactionModal/useNewTransactionModalController.ts index 8c0dba5..804dac8 100644 --- a/frontend/src/view/pages/Dashboard/modals/NewTransactionModal/useNewTransactionModalController.ts +++ b/frontend/src/view/pages/Dashboard/modals/NewTransactionModal/useNewTransactionModalController.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { useDashboard } from "../../components/DashboardContext/useDashboard"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; -import { useBankAccounts } from "../../../../../app/hooks/useBankAccount"; +import { useBankAccounts } from "../../../../../app/hooks/useBankAccounts"; import { useCategories } from "../../../../../app/hooks/useCategories"; import { useMemo } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; @@ -55,13 +55,14 @@ export function useNewTransactionModalController() { }); queryClient.invalidateQueries({ queryKey: ['transactions'] }) + queryClient.invalidateQueries({ queryKey: ['bankAccounts'] }) toast.success( newTransactionType === "EXPENSE" ? "Despesa cadastrada com sucesso!" : "Receita cadastrada com sucesso!" ); closeNewTransactionModal(); - reset() + reset(); } catch { toast.error( newTransactionType === "EXPENSE" diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index d44dcd4..63f09e6 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -200,6 +200,9 @@ export default { 'overlay-show': 'overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1)', 'content-show': 'contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1)', }, + fontFamily: { + sans: ['DM Sans', 'sans-serif'] + }, }, }, plugins: [],