From f715ceafc97a2de81536083b66fb833c8abc999d Mon Sep 17 00:00:00 2001 From: Matt Fiddaman <github@m.fiddaman.uk> Date: Thu, 15 Aug 2024 14:40:20 +0100 Subject: [PATCH] hide target category from cover overspending list (#3115) * hide target category from cover overspending list * release note * suggestions from joel-jeremy * useMemo and better types * add change to mobile * fix build * fix mobile --- .../desktop-client/src/components/Modals.tsx | 1 + .../budget/rollover/BalanceMovementMenu.tsx | 1 + .../components/budget/rollover/CoverMenu.tsx | 40 +++++++++++++++---- .../components/mobile/budget/BudgetTable.jsx | 1 + .../src/components/modals/CoverModal.tsx | 31 +++++++++++++- upcoming-release-notes/3115.md | 6 +++ 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 upcoming-release-notes/3115.md diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index ea1381029..5a45d2159 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -511,6 +511,7 @@ export function Modals() { title={options.title} month={options.month} showToBeBudgeted={options.showToBeBudgeted} + category={options.category} onSubmit={options.onSubmit} /> ); diff --git a/packages/desktop-client/src/components/budget/rollover/BalanceMovementMenu.tsx b/packages/desktop-client/src/components/budget/rollover/BalanceMovementMenu.tsx index 3b8c24408..042dc81f5 100644 --- a/packages/desktop-client/src/components/budget/rollover/BalanceMovementMenu.tsx +++ b/packages/desktop-client/src/components/budget/rollover/BalanceMovementMenu.tsx @@ -59,6 +59,7 @@ export function BalanceMovementMenu({ {menu === 'cover' && ( <CoverMenu + category={categoryId} onClose={onClose} onSubmit={fromCategoryId => { onBudgetAction(month, 'cover-overspending', { diff --git a/packages/desktop-client/src/components/budget/rollover/CoverMenu.tsx b/packages/desktop-client/src/components/budget/rollover/CoverMenu.tsx index b15a28252..1fec28b1e 100644 --- a/packages/desktop-client/src/components/budget/rollover/CoverMenu.tsx +++ b/packages/desktop-client/src/components/budget/rollover/CoverMenu.tsx @@ -1,4 +1,9 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; + +import { + type CategoryGroupEntity, + type CategoryEntity, +} from 'loot-core/src/types/models'; import { useCategories } from '../../../hooks/useCategories'; import { CategoryAutocomplete } from '../../autocomplete/CategoryAutocomplete'; @@ -7,26 +12,47 @@ import { InitialFocus } from '../../common/InitialFocus'; import { View } from '../../common/View'; import { addToBeBudgetedGroup } from '../util'; +function removeSelectedCategory( + categoryGroups: CategoryGroupEntity[], + category?: CategoryEntity['id'], +) { + if (!category) return categoryGroups; + + return categoryGroups + .map(group => ({ + ...group, + categories: group.categories?.filter(cat => cat.id !== category), + })) + .filter(group => group.categories?.length); +} + type CoverMenuProps = { showToBeBudgeted?: boolean; + category?: CategoryEntity['id']; onSubmit: (categoryId: string) => void; onClose: () => void; }; export function CoverMenu({ showToBeBudgeted = true, + category, onSubmit, onClose, }: CoverMenuProps) { const { grouped: originalCategoryGroups } = useCategories(); - const filteredCategoryGroups = originalCategoryGroups.filter( - g => !g.is_income, - ); + const expenseGroups = originalCategoryGroups.filter(g => !g.is_income); + const categoryGroups = showToBeBudgeted - ? addToBeBudgetedGroup(filteredCategoryGroups) - : filteredCategoryGroups; + ? addToBeBudgetedGroup(expenseGroups) + : expenseGroups; + const [categoryId, setCategoryId] = useState<string | null>(null); + const filteredCategoryGroups = useMemo( + () => removeSelectedCategory(categoryGroups, category), + [categoryGroups, category], + ); + function submit() { if (categoryId) { onSubmit(categoryId); @@ -40,7 +66,7 @@ export function CoverMenu({ <InitialFocus> {node => ( <CategoryAutocomplete - categoryGroups={categoryGroups} + categoryGroups={filteredCategoryGroups} value={categoryGroups.find(g => g.id === categoryId) ?? null} openOnFocus={true} onSelect={(id: string | undefined) => setCategoryId(id || null)} diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index a8782b83f..bfd5c5514 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -391,6 +391,7 @@ const ExpenseCategory = memo(function ExpenseCategory({ pushModal('cover', { title: category.name, month, + category: category.id, onSubmit: fromCategoryId => { onBudgetAction(month, 'cover-overspending', { to: category.id, diff --git a/packages/desktop-client/src/components/modals/CoverModal.tsx b/packages/desktop-client/src/components/modals/CoverModal.tsx index acdc38cb5..70ff82bd5 100644 --- a/packages/desktop-client/src/components/modals/CoverModal.tsx +++ b/packages/desktop-client/src/components/modals/CoverModal.tsx @@ -2,6 +2,10 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; +import { + type CategoryGroupEntity, + type CategoryEntity, +} from 'loot-core/src/types/models'; import { useCategories } from '../../hooks/useCategories'; import { useInitialMount } from '../../hooks/useInitialMount'; @@ -12,10 +16,25 @@ import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal2'; import { View } from '../common/View'; import { FieldLabel, TapField } from '../mobile/MobileForms'; +function removeSelectedCategory( + categoryGroups: CategoryGroupEntity[], + category?: CategoryEntity['id'], +) { + if (!category) return categoryGroups; + + return categoryGroups + .map(group => ({ + ...group, + categories: group.categories?.filter(cat => cat.id !== category), + })) + .filter(group => group.categories?.length); +} + type CoverModalProps = { title: string; month: string; showToBeBudgeted?: boolean; + category?: CategoryEntity['id']; onSubmit: (categoryId: string) => void; }; @@ -23,6 +42,7 @@ export function CoverModal({ title, month, showToBeBudgeted = true, + category, onSubmit, }: CoverModalProps) { const { grouped: originalCategoryGroups } = useCategories(); @@ -30,27 +50,34 @@ export function CoverModal({ const filteredCategoryGroups = originalCategoryGroups.filter( g => !g.is_income, ); + const expenseGroups = showToBeBudgeted ? addToBeBudgetedGroup(filteredCategoryGroups) : filteredCategoryGroups; + const expenseCategories = expenseGroups.flatMap(g => g.categories || []); return [expenseGroups, expenseCategories]; }, [originalCategoryGroups, showToBeBudgeted]); + const filteredCategoryGroups = useMemo( + () => removeSelectedCategory(categoryGroups, category), + [categoryGroups, category], + ); + const [fromCategoryId, setFromCategoryId] = useState<string | null>(null); const dispatch = useDispatch(); const onCategoryClick = useCallback(() => { dispatch( pushModal('category-autocomplete', { - categoryGroups, + categoryGroups: filteredCategoryGroups, month, onSelect: categoryId => { setFromCategoryId(categoryId); }, }), ); - }, [categoryGroups, dispatch, month]); + }, [filteredCategoryGroups, dispatch, month]); const _onSubmit = (categoryId: string | null) => { if (categoryId) { diff --git a/upcoming-release-notes/3115.md b/upcoming-release-notes/3115.md new file mode 100644 index 000000000..fd2b7fa1f --- /dev/null +++ b/upcoming-release-notes/3115.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [matt-fidd] +--- + +Hide the target category from the cover overspending category list -- GitLab