From 8c43c78fc7d718d1d6a37d618d2bf568c71aa04c Mon Sep 17 00:00:00 2001 From: Neil <55785687+carkom@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:55:21 +0000 Subject: [PATCH 1/6] Custom reports reorganize table graph files (#2153) * reorg * notes * Update upcoming-release-notes/2153.md Co-authored-by: DJ Mountney * merge fixes * fix * another * f --------- Co-authored-by: DJ Mountney --- .../src/components/reports/ChooseGraph.tsx | 8 ++++---- .../reports/{ => graphs/tableGraph}/ReportTable.tsx | 4 ++-- .../{ => graphs/tableGraph}/ReportTableHeader.tsx | 9 ++++----- .../reports/{ => graphs/tableGraph}/ReportTableList.tsx | 6 +++--- .../{ => graphs/tableGraph}/ReportTableTotals.tsx | 9 ++++----- upcoming-release-notes/2153.md | 6 ++++++ 6 files changed, 23 insertions(+), 19 deletions(-) rename packages/desktop-client/src/components/reports/{ => graphs/tableGraph}/ReportTable.tsx (92%) rename packages/desktop-client/src/components/reports/{ => graphs/tableGraph}/ReportTableHeader.tsx (92%) rename packages/desktop-client/src/components/reports/{ => graphs/tableGraph}/ReportTableList.tsx (97%) rename packages/desktop-client/src/components/reports/{ => graphs/tableGraph}/ReportTableTotals.tsx (95%) create mode 100644 upcoming-release-notes/2153.md diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx index c7baea09b9b..c1da3e8b8a5 100644 --- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx +++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx @@ -9,11 +9,11 @@ import { BarLineGraph } from './graphs/BarLineGraph'; import { DonutGraph } from './graphs/DonutGraph'; import { LineGraph } from './graphs/LineGraph'; import { StackedBarGraph } from './graphs/StackedBarGraph'; +import { ReportTable } from './graphs/tableGraph/ReportTable'; +import { ReportTableHeader } from './graphs/tableGraph/ReportTableHeader'; +import { ReportTableList } from './graphs/tableGraph/ReportTableList'; +import { ReportTableTotals } from './graphs/tableGraph/ReportTableTotals'; import { ReportOptions } from './ReportOptions'; -import { ReportTable } from './ReportTable'; -import { ReportTableHeader } from './ReportTableHeader'; -import { ReportTableList } from './ReportTableList'; -import { ReportTableTotals } from './ReportTableTotals'; type ChooseGraphProps = { data: DataEntity; diff --git a/packages/desktop-client/src/components/reports/ReportTable.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx similarity index 92% rename from packages/desktop-client/src/components/reports/ReportTable.tsx rename to packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx index 389d3e8be23..91a5ca1457a 100644 --- a/packages/desktop-client/src/components/reports/ReportTable.tsx +++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx @@ -6,8 +6,8 @@ import React, { } from 'react'; import { type RefProp } from 'react-spring'; -import { type CSSProperties } from '../../style'; -import { View } from '../common/View'; +import { type CSSProperties } from '../../../../style'; +import { View } from '../../../common/View'; type ReportTableProps = { saveScrollWidth?: (value: number) => void; diff --git a/packages/desktop-client/src/components/reports/ReportTableHeader.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableHeader.tsx similarity index 92% rename from packages/desktop-client/src/components/reports/ReportTableHeader.tsx rename to packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableHeader.tsx index 57078fe32db..c95fb518f41 100644 --- a/packages/desktop-client/src/components/reports/ReportTableHeader.tsx +++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableHeader.tsx @@ -1,11 +1,10 @@ import React, { type UIEventHandler } from 'react'; import { type RefProp } from 'react-spring'; -import { styles, theme } from '../../style'; -import { View } from '../common/View'; -import { Row, Cell } from '../table'; - -import { type MonthData } from './entities'; +import { styles, theme } from '../../../../style'; +import { View } from '../../../common/View'; +import { Row, Cell } from '../../../table'; +import { type MonthData } from '../../entities'; type ReportTableHeaderProps = { scrollWidth?: number; diff --git a/packages/desktop-client/src/components/reports/ReportTableList.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx similarity index 97% rename from packages/desktop-client/src/components/reports/ReportTableList.tsx rename to packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx index eeb6e6fc2e9..0cda809d6b4 100644 --- a/packages/desktop-client/src/components/reports/ReportTableList.tsx +++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx @@ -6,9 +6,9 @@ import { integerToCurrency, } from 'loot-core/src/shared/util'; -import { type CSSProperties, styles, theme } from '../../style'; -import { View } from '../common/View'; -import { Row, Cell } from '../table'; +import { type CSSProperties, styles, theme } from '../../../../style'; +import { View } from '../../../common/View'; +import { Row, Cell } from '../../../table'; type TableRowProps = { item: { diff --git a/packages/desktop-client/src/components/reports/ReportTableTotals.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx similarity index 95% rename from packages/desktop-client/src/components/reports/ReportTableTotals.tsx rename to packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx index df0e5b019e3..be418de80c6 100644 --- a/packages/desktop-client/src/components/reports/ReportTableTotals.tsx +++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx @@ -7,11 +7,10 @@ import { integerToCurrency, } from 'loot-core/src/shared/util'; -import { styles, theme } from '../../style'; -import { View } from '../common/View'; -import { Row, Cell } from '../table'; - -import { type DataEntity } from './entities'; +import { styles, theme } from '../../../../style'; +import { View } from '../../../common/View'; +import { Row, Cell } from '../../../table'; +import { type DataEntity } from '../../entities'; type ReportTableTotalsProps = { data: DataEntity; diff --git a/upcoming-release-notes/2153.md b/upcoming-release-notes/2153.md new file mode 100644 index 00000000000..8fab8116d45 --- /dev/null +++ b/upcoming-release-notes/2153.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [carkom] +--- + +Reorganize tableGraph files for custom reports. From 33e778fe9fa88de21b6e1bff5bac9cc1a9b5b326 Mon Sep 17 00:00:00 2001 From: shall0pass <20625555+shall0pass@users.noreply.github.com> Date: Sun, 7 Jan 2024 16:19:19 -0600 Subject: [PATCH 2/6] [Maintenence] Refactor Goals Schedule file (#2102) * refactor pass 1 * refactor pass 2 * refactor pass 3 * commented out startDate * remove console logging * release note * non-repeating error * add daily * move else * Fix compounding to_budget * lint * reapply 2125 --- .../src/server/budget/goals/goalsSchedule.ts | 295 ++++++++++-------- upcoming-release-notes/2102.md | 6 + 2 files changed, 166 insertions(+), 135 deletions(-) create mode 100644 upcoming-release-notes/2102.md diff --git a/packages/loot-core/src/server/budget/goals/goalsSchedule.ts b/packages/loot-core/src/server/budget/goals/goalsSchedule.ts index b311d5bcf9e..f71a0f7f379 100644 --- a/packages/loot-core/src/server/budget/goals/goalsSchedule.ts +++ b/packages/loot-core/src/server/budget/goals/goalsSchedule.ts @@ -8,82 +8,71 @@ import { } from '../../schedules/app'; import { isReflectBudget } from '../actions'; -export async function goalsSchedule( - scheduleFlag, - template_lines, - current_month, - balance, - remainder, - last_month_balance, - to_budget, - errors, - category, -) { - if (!scheduleFlag) { - scheduleFlag = true; - const template = template_lines.filter(t => t.type === 'schedule'); - //in the case of multiple templates per category, schedules may have wrong priority level - let t = []; - let totalScheduledGoal = 0; +async function createScheduleList(template, current_month, category) { + const t = []; + const errors = []; - for (let ll = 0; ll < template.length; ll++) { - const { id: sid, completed: complete } = await db.first( - 'SELECT * FROM schedules WHERE name = ?', - [template[ll].name], - ); - const rule = await getRuleForSchedule(sid); - const conditions = rule.serialize().conditions; - const { date: dateConditions, amount: amountCondition } = - extractScheduleConds(conditions); - const sign = category.is_income ? 1 : -1; - const target = - amountCondition.op === 'isbetween' - ? (sign * - Math.round( - amountCondition.value.num1 + amountCondition.value.num2, - )) / - 2 - : sign * amountCondition.value; - const next_date_string = getNextDate( - dateConditions, - monthUtils._parse(current_month), - ); - const target_interval = dateConditions.value.interval - ? dateConditions.value.interval - : 1; - const target_frequency = dateConditions.value.frequency; - const isRepeating = - Object(dateConditions.value) === dateConditions.value && - 'frequency' in dateConditions.value; - const num_months = monthUtils.differenceInCalendarMonths( - next_date_string, - current_month, - ); - const startDate = dateConditions.value.start ?? dateConditions.value; - const started = startDate <= monthUtils.addMonths(current_month, 1); + for (let ll = 0; ll < template.length; ll++) { + const { id: sid, completed: complete } = await db.first( + 'SELECT * FROM schedules WHERE name = ? AND tombstone = 0', + [template[ll].name], + ); + const rule = await getRuleForSchedule(sid); + const conditions = rule.serialize().conditions; + const { date: dateConditions, amount: amountCondition } = + extractScheduleConds(conditions); + const sign = category.is_income ? 1 : -1; + const target = + amountCondition.op === 'isbetween' + ? (sign * + Math.round( + amountCondition.value.num1 + amountCondition.value.num2, + )) / + 2 + : sign * amountCondition.value; + const next_date_string = getNextDate( + dateConditions, + monthUtils._parse(current_month), + ); + const target_interval = dateConditions.value.interval + ? dateConditions.value.interval + : 1; + const target_frequency = dateConditions.value.frequency; + const isRepeating = + Object(dateConditions.value) === dateConditions.value && + 'frequency' in dateConditions.value; + const num_months = monthUtils.differenceInCalendarMonths( + next_date_string, + current_month, + ); + if (num_months < 0) { + //non-repeating schedules could be negative + errors.push(`Schedule ${template[ll].name} is in the Past.`); + } else { t.push({ - template: template[ll], target, next_date_string, target_interval, target_frequency, num_months, completed: complete, - started, + //started, + full: template[ll].full === null ? false : template[ll].full, + repeat: isRepeating, + name: template[ll].name, }); - if (!complete && started) { + if (!complete) { if (isRepeating) { let monthlyTarget = 0; const nextMonth = monthUtils.addMonths( current_month, - t[ll].num_months + 1, + t[t.length - 1].num_months + 1, ); let nextBaseDate = getNextDate( dateConditions, monthUtils._parse(current_month), true, ); - let nextDate = dateConditions.value.skipWeekend ? monthUtils.dayFromDate( getDateWithSkippedWeekend( @@ -92,7 +81,6 @@ export async function goalsSchedule( ), ) : nextBaseDate; - while (nextDate < nextMonth) { monthlyTarget += -target; const currentDate = nextBaseDate; @@ -119,92 +107,129 @@ export async function goalsSchedule( break; } } - t[ll].target = -monthlyTarget; - totalScheduledGoal += target; + t[t.length - 1].target = -monthlyTarget; } } else { errors.push( - `Schedule ${t[ll].template.name} is not active during the month in question.`, + `Schedule ${t[ll].name} is not active during the month in question.`, ); } } + } + return { t: t.filter(c => c.completed === 0), errors }; +} - t = t.filter(t => t.completed === 0 && t.started); - t = t.sort((a, b) => b.target - a.target); +async function getPayMonthOfTotal(t) { + //return the contribution amounts of full or every month type schedules + let total = 0; + const schedules = t.filter(c => c.num_months === 0); + for (let ll = 0; ll < schedules.length; ll++) { + total += schedules[ll].target; + } + return total; +} - let increment = 0; - if (balance >= totalScheduledGoal) { - for (let ll = 0; ll < t.length; ll++) { - if (t[ll].num_months < 0) { - errors.push( - `Non-repeating schedule ${t[ll].template.name} was due on ${t[ll].next_date_string}, which is in the past.`, - ); - break; - } - if ( - (t[ll].template.full && t[ll].num_months === 0) || - t[ll].target_frequency === 'weekly' || - t[ll].target_frequency === 'daily' - ) { - increment += t[ll].target; - } else if (t[ll].template.full && t[ll].num_months > 0) { - increment += 0; - } else { - increment += t[ll].target / t[ll].target_interval; - } - } - } else if (balance < totalScheduledGoal) { - for (let ll = 0; ll < t.length; ll++) { - if (isReflectBudget()) { - if (!t[ll].template.full) { - errors.push( - `Report budgets require the full option for Schedules.`, - ); - break; - } - if (t[ll].template.full && t[ll].num_months === 0) { - to_budget += t[ll].target; - } - } - if (!isReflectBudget()) { - if (t[ll].num_months < 0) { - errors.push( - `Non-repeating schedule ${t[ll].template.name} was due on ${t[ll].next_date_string}, which is in the past.`, - ); - break; - } - if (t[ll].template.full && t[ll].num_months > 0) { - remainder = 0; - } else if (ll === 0 && !t[ll].template.full) { - remainder = t[ll].target - last_month_balance; - } else { - remainder = t[ll].target - remainder; - } - let tg = 0; - if (remainder >= 0) { - tg = remainder; - remainder = 0; - } else { - tg = 0; - remainder = Math.abs(remainder); - } - if ( - t[ll].template.full || - t[ll].num_months === 0 || - t[ll].target_frequency === 'weekly' || - t[ll].target_frequency === 'daily' - ) { - increment += tg; - } else if (t[ll].template.full && t[ll].num_months > 0) { - increment += 0; - } else { - increment += tg / (t[ll].num_months + 1); - } - } - } +async function getSinkingContributionTotal(t, remainder, last_month_balance) { + //return the contribution amount if there is a balance carried in the category + let total = 0; + for (let ll = 0; ll < t.length; ll++) { + remainder = + ll === 0 ? t[ll].target - last_month_balance : t[ll].target - remainder; + let tg = 0; + if (remainder >= 0) { + tg = remainder; + remainder = 0; + } else { + tg = 0; + remainder = Math.abs(remainder); + } + total += tg / (t[ll].num_months + 1); + } + return total; +} + +async function getSinkingBaseContributionTotal(t) { + //return only the base contribution of each schedule + let total = 0; + for (let ll = 0; ll < t.length; ll++) { + total += t[ll].target / t[ll].target_interval; + } + return total; +} + +async function getSinkingTotal(t) { + //sum the total of all upcoming schedules + let total = 0; + for (let ll = 0; ll < t.length; ll++) { + total += t[ll].target; + } + return total; +} + +export async function goalsSchedule( + scheduleFlag, + template_lines, + current_month, + balance, + remainder, + last_month_balance, + to_budget, + errors, + category, +) { + if (!scheduleFlag) { + scheduleFlag = true; + const template = template_lines.filter(t => t.type === 'schedule'); + //in the case of multiple templates per category, schedules may have wrong priority level + + const t = await createScheduleList(template, current_month, category); + errors = errors.concat(t.errors); + + const t_payMonthOf = t.t.filter( + c => + c.full || + (c.target_frequency === 'monthly' && + c.target_interval === 1 && + c.num_months === 0) || + (c.target_frequency === 'weekly' && + c.target_interval >= 0 && + c.num_months === 0) || + c.target_frequency === 'daily' || + isReflectBudget(), + ); + + const t_sinking = t.t + .filter( + c => + (!c.full && + c.target_frequency === 'monthly' && + c.target_interval > 1) || + (!c.full && + c.target_frequency === 'monthly' && + c.num_months > 0 && + c.target_interval === 1) || + (!c.full && c.target_frequency === 'yearly') || + (!c.full && c.target_frequency === undefined), + ) + .sort((a, b) => a.next_date_string.localeCompare(b.next_date_string)); + + const totalPayMonthOf = await getPayMonthOfTotal(t_payMonthOf); + + const totalSinking = await getSinkingTotal(t_sinking); + const totalSinkingBaseContribution = await getSinkingBaseContributionTotal( + t_sinking, + ); + + if (balance >= totalSinking + totalPayMonthOf) { + to_budget += Math.round(totalPayMonthOf + totalSinkingBaseContribution); + } else { + const totalSinkingContribution = await getSinkingContributionTotal( + t_sinking, + remainder, + last_month_balance, + ); + to_budget += Math.round(totalPayMonthOf + totalSinkingContribution); } - increment = Math.round(increment); - to_budget += increment; } return { to_budget, errors, remainder, scheduleFlag }; } diff --git a/upcoming-release-notes/2102.md b/upcoming-release-notes/2102.md new file mode 100644 index 00000000000..a632e1dd315 --- /dev/null +++ b/upcoming-release-notes/2102.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [shall0pass] +--- + +Goals: Refactor schedules file into functions and improve the readability of the code. \ No newline at end of file From caca2497ea4e310df89899a8fef3107a83355b70 Mon Sep 17 00:00:00 2001 From: Neil <55785687+carkom@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:51:51 +0000 Subject: [PATCH 3/6] Add ability to import categories from CSV (#2163) * update transaction table * notes * adjust parser --- .../src/components/accounts/Account.jsx | 2 + .../components/modals/ImportTransactions.jsx | 60 +++++++++++++++++-- upcoming-release-notes/2163.md | 6 ++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 upcoming-release-notes/2163.md diff --git a/packages/desktop-client/src/components/accounts/Account.jsx b/packages/desktop-client/src/components/accounts/Account.jsx index a91ad99ee55..e61c46c68ad 100644 --- a/packages/desktop-client/src/components/accounts/Account.jsx +++ b/packages/desktop-client/src/components/accounts/Account.jsx @@ -463,6 +463,7 @@ class AccountInternal extends PureComponent { onImport = async () => { const accountId = this.props.accountId; const account = this.props.accounts.find(acct => acct.id === accountId); + const categories = await this.props.getCategories(); if (account) { const res = await window.Actual.openFileDialog({ @@ -477,6 +478,7 @@ class AccountInternal extends PureComponent { if (res) { this.props.pushModal('import-transactions', { accountId, + categories, filename: res[0], onImported: didChange => { if (didChange) { diff --git a/packages/desktop-client/src/components/modals/ImportTransactions.jsx b/packages/desktop-client/src/components/modals/ImportTransactions.jsx index 8c55e3b322e..79f23a37a84 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactions.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactions.jsx @@ -194,14 +194,24 @@ function getInitialMappings(transactions) { fields.find(([name, value]) => value.match(/^-?[.,\d]+$/)), ); + const categoryField = key( + fields.find(([name, value]) => name.toLowerCase().includes('category')), + ); + const payeeField = key( - fields.find(([name, value]) => name !== dateField && name !== amountField), + fields.find( + ([name, value]) => + name !== dateField && name !== amountField && name !== categoryField, + ), ); const notesField = key( fields.find( ([name, value]) => - name !== dateField && name !== amountField && name !== payeeField, + name !== dateField && + name !== amountField && + name !== categoryField && + name !== payeeField, ), ); @@ -221,6 +231,7 @@ function getInitialMappings(transactions) { payee: payeeField, notes: notesField, inOut: inOutField, + category: categoryField, }; } @@ -290,6 +301,19 @@ function parseAmountFields( }; } +function parseCategoryFields(trans, categories) { + let match = null; + categories.forEach(category => { + if (category.id === trans.category) { + return null; + } + if (category.name === trans.category) { + match = category.id; + } + }); + return match; +} + function Transaction({ transaction: rawTransaction, fieldMappings, @@ -301,7 +325,9 @@ function Transaction({ outValue, flipAmount, multiplierAmount, + categories, }) { + const categoryList = categories.map(category => category.name); const transaction = useMemo( () => fieldMappings @@ -348,6 +374,14 @@ function Transaction({ {transaction.notes} + + {categoryList.includes(transaction.category) && transaction.category} + {splitMode ? ( <> + + + onChange('category', name)} + hasHeaderRow={hasHeaderRow} + firstTransaction={transactions[0]} + /> + {splitMode ? ( <> @@ -676,7 +721,7 @@ export function ImportTransactions({ modalProps, options }) { const [outValue, setOutValue] = useState(''); const [flipAmount, setFlipAmount] = useState(false); const [multiplierEnabled, setMultiplierEnabled] = useState(false); - const { accountId, onImported } = options; + const { accountId, categories, onImported } = options; // This cannot be set after parsing the file, because changing it // requires re-parsing the file. This is different from the other @@ -866,6 +911,11 @@ export function ImportTransactions({ modalProps, options }) { break; } + const category_id = parseCategoryFields(trans, categories.list); + if (category_id != null) { + trans.category = category_id; + } + const { inflow, outflow, inOut, ...finalTransaction } = trans; finalTransactions.push({ ...finalTransaction, @@ -919,6 +969,7 @@ export function ImportTransactions({ modalProps, options }) { { name: 'Date', width: 200 }, { name: 'Payee', width: 'flex' }, { name: 'Notes', width: 'flex' }, + { name: 'Category', width: 'flex' }, ]; if (inOutMode) { @@ -959,7 +1010,7 @@ export function ImportTransactions({ modalProps, options }) { index} renderEmpty={() => { @@ -989,6 +1040,7 @@ export function ImportTransactions({ modalProps, options }) { outValue={outValue} flipAmount={flipAmount} multiplierAmount={multiplierAmount} + categories={categories.list} /> )} diff --git a/upcoming-release-notes/2163.md b/upcoming-release-notes/2163.md new file mode 100644 index 00000000000..2d7313306ba --- /dev/null +++ b/upcoming-release-notes/2163.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [ScottFries, blakegearin, carkom] +--- + +Add ability to import categories from CSV \ No newline at end of file From 300ed824f03bd4ddc3eb9d9983f1eab2af4a5ce2 Mon Sep 17 00:00:00 2001 From: youngcw Date: Mon, 8 Jan 2024 11:20:29 -0700 Subject: [PATCH 4/6] change the pie chart background color in the report status (#2196) * change the pie chart background color in the report status * note --- .../budget/report/budgetsummary/ExpenseProgress.tsx | 2 +- .../budget/report/budgetsummary/IncomeProgress.tsx | 2 +- upcoming-release-notes/2196.md | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 upcoming-release-notes/2196.md diff --git a/packages/desktop-client/src/components/budget/report/budgetsummary/ExpenseProgress.tsx b/packages/desktop-client/src/components/budget/report/budgetsummary/ExpenseProgress.tsx index 8e0b7c6cb46..3aeb3312039 100644 --- a/packages/desktop-client/src/components/budget/report/budgetsummary/ExpenseProgress.tsx +++ b/packages/desktop-client/src/components/budget/report/budgetsummary/ExpenseProgress.tsx @@ -34,7 +34,7 @@ export function ExpenseProgress({ current, target }: ExpenseProgressProps) { ); diff --git a/packages/desktop-client/src/components/budget/report/budgetsummary/IncomeProgress.tsx b/packages/desktop-client/src/components/budget/report/budgetsummary/IncomeProgress.tsx index 66b64abd50c..f264b07e9d8 100644 --- a/packages/desktop-client/src/components/budget/report/budgetsummary/IncomeProgress.tsx +++ b/packages/desktop-client/src/components/budget/report/budgetsummary/IncomeProgress.tsx @@ -28,7 +28,7 @@ export function IncomeProgress({ current, target }: IncomeProgressProps) { ); diff --git a/upcoming-release-notes/2196.md b/upcoming-release-notes/2196.md new file mode 100644 index 00000000000..8ef2f5e0a80 --- /dev/null +++ b/upcoming-release-notes/2196.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [youngcw] +--- + +Improve report budget pie chart colors From fd962a97b09dc4addb53b48f8d2a29e5b3b2494e Mon Sep 17 00:00:00 2001 From: youngcw Date: Mon, 8 Jan 2024 11:20:52 -0700 Subject: [PATCH 5/6] fix missing borders in single category drop down in report budget (#2195) * fix missing borders in single category drop down in report budget * note --- .../src/components/budget/report/ReportComponents.tsx | 6 ++++-- upcoming-release-notes/2195.md | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 upcoming-release-notes/2195.md diff --git a/packages/desktop-client/src/components/budget/report/ReportComponents.tsx b/packages/desktop-client/src/components/budget/report/ReportComponents.tsx index b50cb9171a0..b33faba456d 100644 --- a/packages/desktop-client/src/components/budget/report/ReportComponents.tsx +++ b/packages/desktop-client/src/components/budget/report/ReportComponents.tsx @@ -189,9 +189,11 @@ export const CategoryMonth = memo(function CategoryMonth({