Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/ChartSwitcher/ExpenseChart.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts';
import { avatarBackgroundColors } from '../../mockData/colorsPallete/colors';
import { useMeDashboardQuery } from '../../hooks/ReactQuery/useMeDashboardStatsQuery';
import { useMeDashboardQuery } from '../../hooks/ReactQuery/Queries/useMeDashboardStatsQuery';

export default function ExpenseQuantityDonutChart() {
const { data: expenseData } = useMeDashboardQuery();
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/LatestExpenses/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
formatDateBR,
formatRelativeDate,
} from '../../utils/formatters';
import useMeExpensesQuery from '../../hooks/ReactQuery/useMeExpensesQuery';

import Pagination from '../PaginationController';
import useMeExpensesQuery from '../../hooks/ReactQuery/Queries/useMeExpensesQuery';

export default function LatestExpenses() {
// 🔢 Estado da paginação
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Layout/DropdownMenu/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import SwitchTheme from './SwitchTheme';
import { FaMoon, FaSun } from 'react-icons/fa';
import { useLogout } from '../../../hooks/useLogout';
import useMeQuery from '../../../hooks/ReactQuery/useMeQuery';
import useMeQuery from '../../../hooks/ReactQuery/Queries/useMeQuery';

export default function DropdownMenu() {
const logout = useLogout();
const { data: user, isLoading, error } = useMeQuery();

Check failure on line 10 in frontend/src/components/Layout/DropdownMenu/index.jsx

View workflow job for this annotation

GitHub Actions / Frontend (React)

'isLoading' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u

const firstLetter = user?.name[0]?.toUpperCase() ?? '?';

return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Modal/ModalButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useState } from 'react';
import ButtonUI from '../ui/Button';
import Modal from '.';

export default function ModalButton({ modalChildren }) {
const [isOpenModal, setIsOpenModal] = useState(false);
export default function ModalButton({ modalChildren, openState = false }) {
const [isOpenModal, setIsOpenModal] = useState(openState);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SelectUI from '../../../ui/Select';
import TextareaUI from '../../../ui/Textarea';
import CurrencyInputUI from '../../../ui/CurrencyInput';
import { useFormExpense } from '../useForm';
import { useAllGroupExpenseCategoriesQuery } from '../../../../hooks/ReactQuery/useAllGroupExpenseCategoriesQuery';
import { useAllGroupExpenseCategoriesQuery } from '../../../../hooks/ReactQuery/Queries/useAllGroupExpenseCategoriesQuery';

export default function FormFieldsExpenses() {
const { data: allCategories } = useAllGroupExpenseCategoriesQuery();
Expand Down
27 changes: 7 additions & 20 deletions frontend/src/components/forms/ExpenseForm/ListMembers/index.jsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
import { useState, useEffect, useMemo } from 'react';
import { useState, useEffect } from 'react';
import { FiSearch } from 'react-icons/fi';
import ButtonUI from '../../../ui/Button';

import useMeQuery from '../../../../hooks/ReactQuery/useMeQuery';
import { useMembersQuery } from '../../../../hooks/ReactQuery/useMembersQuery';
import InputUI from '../../../ui/Input';
import Pagination from '../../../PaginationController';

export default function ListMembers({ groupID, onClose, onConfirm }) {
const [search, setSearch] = useState(''); // termo final para API
export default function ListMembers({
isLoading,
members: { filteredMembers, totalPages },
onConfirm,
search: { search, setSearch },
}) {
const [inputValue, setInputValue] = useState(''); // valor do input enquanto digita
const [page, setPage] = useState(0);

const { data: user } = useMeQuery();
const {
data: { members, totalPages } = { members: [], totalPages: 0 },
isLoading,
} = useMembersQuery(groupID, page, search);

const [checkedMembers, setCheckedMembers] = useState([]);

const excludeSelf = false; // se excluir da lista de membros

const filteredMembers = useMemo(() => {
if (!members) return [];
return excludeSelf ? members.filter(m => m.id !== user?.id) : members;
}, [members, user, excludeSelf]);

// Alterna seleção de membros
const toggleMember = id => {
setCheckedMembers(prev =>
Expand All @@ -42,7 +29,7 @@
}, 500);

return () => clearTimeout(handler);
}, [inputValue]); // 👈 apenas inputValue aqui

Check warning on line 32 in frontend/src/components/forms/ExpenseForm/ListMembers/index.jsx

View workflow job for this annotation

GitHub Actions / Frontend (React)

React Hook useEffect has a missing dependency: 'setSearch'. Either include it or remove the dependency array. If 'setSearch' changes too often, find the parent component that defines it and wrap that definition in useCallback

// Reseta seleção ao mudar de página ou busca
useEffect(() => {
Expand Down
43 changes: 28 additions & 15 deletions frontend/src/components/forms/ExpenseForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,31 @@
import ButtonUI from '../../ui/Button';
import CustomSplitAmount from './CustomSplitAmount';
import { useFormExpense } from './useForm';
import { useEffect, useState, useCallback } from 'react';
import { useEffect, useState, useCallback, useMemo } from 'react';
import { customToast } from '../../CustomToast';
import Modal from '../../Modal';
import ListMembers from './ListMembers';
import { FiUserPlus, FiX } from 'react-icons/fi';
/* import useMeQuery from '../../../hooks/ReactQuery/useMeQuery'; */
import { useMembersQuery } from '../../../hooks/ReactQuery/useMembersQuery';
import { useCreateExpenseMutation } from '../../../hooks/ReactQuery/useCreateExpenseMutation';
import useMeQuery from '../../../hooks/ReactQuery/Queries/useMeQuery';
import { useCreateExpenseMutation } from '../../../hooks/ReactQuery/Mutations/useCreateExpenseMutation';
import { useMembersQuery } from '../../../hooks/ReactQuery/Queries/useMembersQuery';

export default function ExpenseForm({ groupData: group }) {
/* const { data: user } = useMeQuery(); */
const [search, setSearch] = useState(''); // termo final para API
const { data: user } = useMeQuery();
const useExpenseMutation = useCreateExpenseMutation(group?.id);
const {
data: { members: groupMembers },
} = useMembersQuery(group?.id, 0, '');
const { data: { members: groupMembers = [], totalPages } = {}, isLoading } =
useMembersQuery(group?.id, 0, search);

const excludeSelf = true; // 🔧 se quiser incluir o próprio usuário, basta trocar para false

// 🔍 Aplica filtro de exclusão do próprio usuário
const filteredMembers = useMemo(() => {
if (!groupMembers?.length) return [];
return excludeSelf
? groupMembers.filter(m => m.id !== user?.id)
: groupMembers;
}, [groupMembers, user, excludeSelf]);

const { setMembers, amount, divisionAmount, reset, getRemainingDifference } =
useFormExpense();
Expand Down Expand Up @@ -96,7 +106,7 @@
setIsSubmitting(false);
}
},
[amount, divisionAmount, distributionOK]

Check warning on line 109 in frontend/src/components/forms/ExpenseForm/index.jsx

View workflow job for this annotation

GitHub Actions / Frontend (React)

React Hook useCallback has a missing dependency: 'useExpenseMutation'. Either include it or remove the dependency array
);

const removeMember = memberId => {
Expand All @@ -123,11 +133,11 @@
aria-label="Formulário para adicionar nova despesa"
>
{/* Campos principais */}
<FormFieldsExpenses data={groupMembers} />
<FormFieldsExpenses data={filteredMembers} />

{/* Seleção de membros */}
<div className="col-span-2 flex flex-col gap-2">
{selectedMembers.length === 0 && groupMembers?.length > 0 && (
{selectedMembers.length === 0 && filteredMembers.length > 0 && (
<ButtonUI
type="button"
onClick={() => setShowMemberModal(true)}
Expand Down Expand Up @@ -178,10 +188,11 @@
type="submit"
disabled={isSubmitting || !distributionOK || amount <= 0}
aria-disabled={isSubmitting || !distributionOK || amount <= 0}
className={`
bg-primary hover:bg-secondary text-white py-3 px-6 rounded w-full sm:w-auto font-semibold transition
${isSubmitting || !distributionOK || amount <= 0 ? 'opacity-50 bg-gray-400! cursor-not-allowed!' : 'cursor-pointer'}
`}
className={`bg-primary hover:bg-secondary text-white py-3 px-6 rounded w-full sm:w-auto font-semibold transition ${
isSubmitting || !distributionOK || amount <= 0
? 'opacity-50 bg-gray-400! cursor-not-allowed!'
: 'cursor-pointer'
}`}
>
{isSubmitting ? 'Enviando...' : 'Adicionar Despesa'}
</ButtonUI>
Expand All @@ -191,12 +202,14 @@
{/* Modal de seleção */}
<Modal isOpen={showMemberModal} fnClose={() => setShowMemberModal(false)}>
<ListMembers
groupID={group?.id}
members={{ filteredMembers, totalPages }}
search={{ search, setSearch }}
onConfirm={selected => {
setSelectedMembers(selected);
setMembers(selected);
setShowMemberModal(false);
}}
isLoading={isLoading}
onClose={() => setShowMemberModal(false)}
/>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { createExpense } from '../../../services/expenses';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';

export function useCreateExpenseMutation(groupID) {
const queryClient = useQueryClient();

return useMutation({
mutationFn: expenseData => createExpense(groupID, expenseData),

onSuccess: () => {
// 🔄 Invalida TODAS as queries de despesas desse grupo
queryClient.invalidateQueries({
predicate: query => {
const [key, params] = query.queryKey;

return (
key === REACTQUERY_KEYS.GROUPS.EXPENSES &&
typeof params === 'object' &&
params?.groupID === groupID
);
},
});

// 🔄 Atualiza listagem geral de grupos
queryClient.invalidateQueries({
queryKey: [REACTQUERY_KEYS.GROUPS.ALL],
});
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { deleteGroup } from '../../../services/groups';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { useNavigate } from 'react-router';
import { customToast } from '../../../components/CustomToast';

export function useDeleteGroupMutation(id) {
const navigate = useNavigate();
const queryClient = useQueryClient();

return useMutation({
mutationFn: () => deleteGroup(id),
onSuccess: () => {
// Invalida a lista de grupos para forçar um refetch
queryClient.invalidateQueries({
predicate: query =>
query.queryKey[0] === REACTQUERY_KEYS.GROUPS.ALL &&
query.queryKey[1]?.groupID === id, // só o id importa
});
queryClient.invalidateQueries({
predicate: query =>
query.queryKey[0] === REACTQUERY_KEYS.GROUPS.EXPENSES &&
query.queryKey[1]?.groupID === id, // só o id importa
});
queryClient.invalidateQueries([REACTQUERY_KEYS.GROUPS.MEMBERS, id]);

// Opcional: remover manualmente do cache local
queryClient.setQueryData([REACTQUERY_KEYS.GROUPS.ALL], oldData => {
if (!oldData) return oldData;
return {
...oldData,
content: oldData.content.filter(group => group.id !== id),
totalElements: oldData.totalElements - 1,
};
});

customToast('Exclusão', 'Grupo excluído com sucesso', 'success');
navigate('/groups');
},
onError: error => {
customToast(
'Erro',
error.response.data.message || 'Erro ao excluir grupo.',
'error'
);
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function useUpdateGroupExpenseByIdMutation() {
return;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { updateMe } from '../../services/me';
import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { updateMe } from '../../../services/me';

export function useUpdateMeMutation() {
const queryClient = useQueryClient();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys';
import { getAllGroupsExpenseCategories } from '../../services/groups';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getAllGroupsExpenseCategories } from '../../../services/groups';

export function useAllGroupExpenseCategoriesQuery() {
return useQuery({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getGroupExpenseById } from '../../../services/groups';

export function useGroupExpenseByIdQuery(group_id, expense_id) {
return useQuery({
queryKey: [REACTQUERY_KEYS.GROUPS.EXPENSES, { group_id, expense_id }],
enabled: !!group_id && !!expense_id,
queryFn: async () => await getGroupExpenseById(group_id, expense_id),
staleTime: Infinity,
placeholderData: {
expenseId: null,
title: '',
description: '',
groupId: null,
groupName: '',
status: 'PENDING',
total: 0,
categoryId: null,
categoryName: '',
createdAt: new Date().toISOString(),
memberList: [],
},
});
}
36 changes: 36 additions & 0 deletions frontend/src/hooks/ReactQuery/Queries/useGroupExpensesQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getGroupExpenses } from '../../../services/groups';

export function useGroupExpensesQuery(groupID, page, filter) {
// 1. Desestrutura as propriedades de filtro para uso no queryKey
// Assumimos que 'status' e 'allGroupMembersExpenses' são as chaves
// que podem vir do objeto 'filter'.
const { status, allGroupMembersExpenses } = filter;

return useQuery({
// 2. O queryKey agora inclui explicitamente o 'status' e 'allGroupMembersExpenses'.
// Uma mudança em qualquer um desses valores fará com que o array do queryKey mude,
// forçando o TanStack Query a fazer uma nova busca (refetch).
queryKey: [
REACTQUERY_KEYS.GROUPS.EXPENSES,
{
groupID,
page,
status, // Chave do filtro 1
allGroupMembersExpenses, // Chave do filtro 2 (se existir)
},
],
// 3. A queryFn continua usando o objeto 'filter' completo para a chamada de API
queryFn: async () => await getGroupExpenses({ groupID, page, filter }),

// Configurações mantidas
enabled: !!groupID,
staleTime: Infinity,
placeholderData: {
expenses: [],
totalPages: 0,
expensesLength: 0,
},
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys';
import { getGroupById, getGroups } from '../../services/groups';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getGroupById, getGroups } from '../../../services/groups';

export function useGroupsQuery(page) {
return useQuery({
Expand All @@ -14,12 +14,13 @@ export function useGroupsQuery(page) {
},
});
}

export function useGroupByIdQuery(id) {
return useQuery({
queryKey: [REACTQUERY_KEYS.GROUPS.ALL, id],
enabled: !!id,
queryFn: async () => await getGroupById(id),
staleTime: Infinity,
placeholderData: { content: {} },
placeholderData: {},
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys';
import { getMeDashboardStats } from '../../services/me';

import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getMeDashboardStats } from '../../../services/me';

export function useMeDashboardQuery() {
return useQuery({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys';
import { getMeExpenses } from '../../services/me';
import useMeQuery from './useMeQuery';
import { REACTQUERY_KEYS } from '../../../libs/ReactQuery/keys';
import { getMeExpenses } from '../../../services/me';

export default function useMeExpensesQuery(page) {
const { data: user } = useMeQuery();
Expand Down
Loading
Loading