@mx-cartographer/experiences 9.4.42 → 9.4.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/budgets/components/BudgetsEmptyState.es.js +50 -44
- package/dist/budgets/components/BudgetsEmptyState.es.js.map +1 -1
- package/dist/categories/components/CategoryList.es.js +34 -27
- package/dist/categories/components/CategoryList.es.js.map +1 -1
- package/dist/common/components/listitems/CollapseExpandListItem.es.js +26 -25
- package/dist/common/components/listitems/CollapseExpandListItem.es.js.map +1 -1
- package/dist/core/stores/TransactionStore.es.js +1 -1
- package/dist/core/stores/TransactionStore.es.js.map +1 -1
- package/dist/core/types/localization/CommonCopy.d.ts +2 -0
- package/dist/core/types/localization/TrendsCopy.d.ts +0 -1
- package/dist/core/utils/CategoryUtil.d.ts +11 -0
- package/dist/core/utils/CategoryUtil.es.js +36 -32
- package/dist/core/utils/CategoryUtil.es.js.map +1 -1
- package/dist/core/utils/NetWorthUtils.d.ts +2 -2
- package/dist/core/utils/NetWorthUtils.es.js +64 -64
- package/dist/core/utils/NetWorthUtils.es.js.map +1 -1
- package/dist/core/utils/TransactionUtils.d.ts +2 -2
- package/dist/core/utils/TransactionUtils.es.js +93 -92
- package/dist/core/utils/TransactionUtils.es.js.map +1 -1
- package/dist/debts/components/debtstable/AccountCell.es.js +13 -13
- package/dist/debts/components/debtstable/AccountCell.es.js.map +1 -1
- package/dist/finstrong/components/SpendLessThanYouMake/SpendLessThanYouMakeComponent.es.js +40 -36
- package/dist/finstrong/components/SpendLessThanYouMake/SpendLessThanYouMakeComponent.es.js.map +1 -1
- package/dist/networth/components/AssetsLiabilitiesDetail.es.js +47 -47
- package/dist/networth/components/AssetsLiabilitiesDetail.es.js.map +1 -1
- package/dist/spending/components/MiniSpendingChart.es.js +52 -47
- package/dist/spending/components/MiniSpendingChart.es.js.map +1 -1
- package/dist/spending/components/Spending.es.js +82 -74
- package/dist/spending/components/Spending.es.js.map +1 -1
- package/dist/spending/utils/SpendingData.d.ts +7 -5
- package/dist/spending/utils/SpendingData.es.js +34 -31
- package/dist/spending/utils/SpendingData.es.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,116 +1,117 @@
|
|
|
1
|
-
import { CategoryGuidToName as y, CategoryGuids as
|
|
1
|
+
import { CategoryGuidToName as y, CategoryGuids as h } from "../constants/Category.es.js";
|
|
2
2
|
import { DATE_FORMATS_INTL as m, formatDate as f } from "../constants/DateFormats.es.js";
|
|
3
|
-
import { TransactionStatus as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
3
|
+
import { TransactionStatus as C, TransactionType as v } from "../types/Transaction.es.js";
|
|
4
|
+
import { getLocalizedCategoryName as A } from "./CategoryUtil.es.js";
|
|
5
|
+
import { formatCurrency as E } from "./NumberFormatting.es.js";
|
|
6
|
+
import { fromUnixUTC as S } from "./DateUtil.es.js";
|
|
7
|
+
import { TABLE_CONST as b } from "../constants/Table.es.js";
|
|
8
|
+
import { COLUMN_NAMES as _ } from "../constants/Transaction.es.js";
|
|
9
|
+
import { format as O } from "date-fns/format";
|
|
10
|
+
import { startOfMonth as D } from "date-fns/startOfMonth";
|
|
11
|
+
import { startOfToday as N } from "date-fns/startOfToday";
|
|
11
12
|
import { endOfToday as M } from "date-fns/endOfToday";
|
|
12
|
-
import { fromUnixTime as
|
|
13
|
-
import { getUnixTime as
|
|
14
|
-
var
|
|
15
|
-
const
|
|
16
|
-
...
|
|
17
|
-
[
|
|
18
|
-
}), {}),
|
|
19
|
-
...
|
|
20
|
-
[
|
|
21
|
-
}), {}),
|
|
22
|
-
const { transaction_guid: u, tag_guid:
|
|
23
|
-
if (!
|
|
24
|
-
...
|
|
25
|
-
[u]: [
|
|
13
|
+
import { fromUnixTime as U } from "date-fns/fromUnixTime";
|
|
14
|
+
import { getUnixTime as T } from "date-fns/getUnixTime";
|
|
15
|
+
var te = (t) => t.top_level_category_guid === h.TRANSFER, re = (t, r, o, d = [], i) => {
|
|
16
|
+
const c = r.reduce((e, s) => ({
|
|
17
|
+
...e,
|
|
18
|
+
[s.guid]: s
|
|
19
|
+
}), {}), n = o.reduce((e, s) => ({
|
|
20
|
+
...e,
|
|
21
|
+
[s.guid]: s
|
|
22
|
+
}), {}), a = d.reduce((e, s) => {
|
|
23
|
+
const { transaction_guid: u, tag_guid: g } = s;
|
|
24
|
+
if (!e[u]) return {
|
|
25
|
+
...e,
|
|
26
|
+
[u]: [g]
|
|
26
27
|
};
|
|
27
|
-
const
|
|
28
|
+
const l = [...e[u], g];
|
|
28
29
|
return {
|
|
29
|
-
...
|
|
30
|
-
[u]:
|
|
30
|
+
...e,
|
|
31
|
+
[u]: l
|
|
31
32
|
};
|
|
32
33
|
}, {});
|
|
33
|
-
return t.map((
|
|
34
|
-
const
|
|
34
|
+
return t.map((e) => {
|
|
35
|
+
const s = c[e.account_guid], u = e.category_guid ? n[e.category_guid] : null, g = a[e.guid] ?? [];
|
|
35
36
|
return {
|
|
36
|
-
...
|
|
37
|
-
account:
|
|
38
|
-
accountIsClosed:
|
|
39
|
-
accountIsHidden:
|
|
40
|
-
category: u
|
|
41
|
-
institutionName:
|
|
42
|
-
isIncome:
|
|
43
|
-
isPending:
|
|
44
|
-
number:
|
|
45
|
-
payee:
|
|
46
|
-
tags:
|
|
37
|
+
...e,
|
|
38
|
+
account: s?.user_name || "Unknown",
|
|
39
|
+
accountIsClosed: s?.is_closed,
|
|
40
|
+
accountIsHidden: s?.is_hidden,
|
|
41
|
+
category: u ? A(u, i) : "Uncategorized",
|
|
42
|
+
institutionName: s?.institution?.name || s?.institutionName || void 0,
|
|
43
|
+
isIncome: e.top_level_category_guid === h.INCOME,
|
|
44
|
+
isPending: e.feed_status === C.PENDING,
|
|
45
|
+
number: s?.account_number || "",
|
|
46
|
+
payee: e.description,
|
|
47
|
+
tags: g
|
|
47
48
|
};
|
|
48
49
|
});
|
|
49
|
-
},
|
|
50
|
-
const
|
|
50
|
+
}, L = "uncategorized", oe = (t, r, o, d, i, c) => {
|
|
51
|
+
const n = R(t, i, c), a = I(n, o, d);
|
|
51
52
|
return r.forEach((e) => {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
}),
|
|
55
|
-
}, R = (t, r,
|
|
56
|
-
const { accounts:
|
|
57
|
-
start:
|
|
53
|
+
const s = a.findIndex((g) => g.guid === e), u = c.filter((g) => g.parent_guid === e);
|
|
54
|
+
a.splice(s + 1, 0, ...u);
|
|
55
|
+
}), a;
|
|
56
|
+
}, R = (t, r, o) => {
|
|
57
|
+
const { accounts: d = [], dateRange: i = {
|
|
58
|
+
start: D(N()),
|
|
58
59
|
end: M()
|
|
59
|
-
}, custom:
|
|
60
|
-
return
|
|
61
|
-
if (
|
|
62
|
-
if (/^spl/i.test(
|
|
63
|
-
const
|
|
64
|
-
return p(
|
|
60
|
+
}, custom: c = () => !0, searchTerm: n = "", showSplits: a = !1 } = t;
|
|
61
|
+
return o.filter((e) => a ? !e.has_been_split : !e.parent_guid).filter((e) => !!d.find((s) => s === e.account_guid)).filter((e) => e.date >= T(i.start) && e.date <= T(i.end)).filter(c).filter((e) => {
|
|
62
|
+
if (n === L && e.has_been_split) return !1;
|
|
63
|
+
if (/^spl/i.test(n) && (e.has_been_split || e.parent_guid)) return !0;
|
|
64
|
+
const s = e.tags.some((g) => p(n, r.find((l) => l.guid === g)?.name)), u = U(e.date);
|
|
65
|
+
return p(n, e.payee) || p(n, e.feed_description) || p(n, e.category) || p(n, f(u, m.MONTH)) || p(n, f(u, m.DAY)) || p(n, f(u, m.FULL_MONTH_DAY)) || p(n, f(u, m.MONTH_DAY_YEAR)) || p(n, O(u, "do")) || p(n, e.account) || p(n, E(e.amount, "0,0.00")) || p(n, e.memo) || s;
|
|
65
66
|
});
|
|
66
|
-
}, I = (t, r,
|
|
67
|
-
const
|
|
68
|
-
let
|
|
69
|
-
return (r ===
|
|
67
|
+
}, I = (t, r, o) => [...t].sort((d, i) => {
|
|
68
|
+
const c = r, n = (d[c] || "") > (i[c] || "");
|
|
69
|
+
let a = n ? -1 : 1, e = n ? 1 : -1;
|
|
70
|
+
return (r === _.DATE || r === _.AMOUNT) && (a = n ? 1 : -1, e = n ? -1 : 1), o === b.ASC ? a : e;
|
|
70
71
|
}), p = (t, r) => {
|
|
71
|
-
const
|
|
72
|
-
return
|
|
72
|
+
const o = t.toLowerCase().split(" "), d = r?.toString().toLowerCase() ?? "";
|
|
73
|
+
return o.every((i) => d.indexOf(i) > -1);
|
|
73
74
|
};
|
|
74
|
-
function
|
|
75
|
+
function w(t) {
|
|
75
76
|
return new Set(t.filter((r) => r.parent_guid !== void 0).map((r) => r.parent_guid));
|
|
76
77
|
}
|
|
77
|
-
function
|
|
78
|
-
return t.reduce((r,
|
|
78
|
+
function j(t) {
|
|
79
|
+
return t.reduce((r, o) => (o.parent_guid && (r[o.parent_guid] || (r[o.parent_guid] = []), r[o.parent_guid].push(o)), r), {});
|
|
79
80
|
}
|
|
80
|
-
function
|
|
81
|
-
const
|
|
82
|
-
for (const
|
|
83
|
-
|
|
84
|
-
}) :
|
|
85
|
-
return
|
|
81
|
+
function ne(t, r) {
|
|
82
|
+
const o = w(r), d = j(r), i = /* @__PURE__ */ new Set(), c = [];
|
|
83
|
+
for (const n of t) o.has(n.guid) ? (d[n.guid] ?? []).slice().sort((a, e) => Math.abs(e.amount) - Math.abs(a.amount)).forEach((a) => {
|
|
84
|
+
i.has(a.guid) || (c.push(a), i.add(a.guid));
|
|
85
|
+
}) : i.has(n.guid) || (c.push(n), i.add(n.guid));
|
|
86
|
+
return c;
|
|
86
87
|
}
|
|
87
|
-
var
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
Date: f(
|
|
92
|
-
Description:
|
|
93
|
-
"Original Description":
|
|
94
|
-
Amount: `${
|
|
95
|
-
Type:
|
|
96
|
-
"Parent Category": y[
|
|
97
|
-
Category:
|
|
98
|
-
Account:
|
|
99
|
-
Tags:
|
|
100
|
-
Memo:
|
|
101
|
-
Pending:
|
|
102
|
-
})),
|
|
103
|
-
`) + t.map((r) => Object.values(r).map(
|
|
104
|
-
`),
|
|
88
|
+
var ie = (t, r) => {
|
|
89
|
+
const o = x(G(t, r)), d = new Blob([o], { type: "text/csv;charset=utf-8;" }), i = document.createElement("a");
|
|
90
|
+
i.href = URL.createObjectURL(d), i.setAttribute("download", "transactions.csv"), document.body.appendChild(i), i.click(), document.body.removeChild(i), URL.revokeObjectURL(i.href);
|
|
91
|
+
}, G = (t, r) => t.map((o) => ({
|
|
92
|
+
Date: f(S(o.date), m.YEAR_MONTH_DAY),
|
|
93
|
+
Description: F(o),
|
|
94
|
+
"Original Description": o.feed_description,
|
|
95
|
+
Amount: `${o.amount}`,
|
|
96
|
+
Type: o.transaction_type === v.CREDIT ? "Credit" : "Debit",
|
|
97
|
+
"Parent Category": y[o.top_level_category_guid || ""],
|
|
98
|
+
Category: o.category,
|
|
99
|
+
Account: o.account,
|
|
100
|
+
Tags: B(o, r),
|
|
101
|
+
Memo: o.memo,
|
|
102
|
+
Pending: o.isPending ? "TRUE" : "FALSE"
|
|
103
|
+
})), P = (t) => t ? /[,"\n\r]/.test(t) ? `"${t.replace(/"/g, '""')}"` : t : "", x = (t) => t.length === 0 ? "" : Object.keys(t[0]).join(",").concat(`\r
|
|
104
|
+
`) + t.map((r) => Object.values(r).map(P).join(",")).join(`\r
|
|
105
|
+
`), F = (t) => {
|
|
105
106
|
let r = t.parent_guid ? "Split: " : "";
|
|
106
107
|
return r += t.is_hidden ? "(Excluded) " : "", r += t.description, r;
|
|
107
|
-
},
|
|
108
|
+
}, B = (t, r) => t.tags.map((o) => r.filter((d) => d.guid === o)[0]?.name ?? "").join(" ");
|
|
108
109
|
export {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
re as buildDetailedTransactions,
|
|
111
|
+
ie as exportTransactionsToCSV,
|
|
112
|
+
ne as flattenTransactionsWithSplits,
|
|
113
|
+
te as isTransferTransaction,
|
|
114
|
+
oe as sortAndFilterTransactions
|
|
114
115
|
};
|
|
115
116
|
|
|
116
117
|
//# sourceMappingURL=TransactionUtils.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionUtils.es.js","names":[],"sources":["../../../src/core/utils/TransactionUtils.ts"],"sourcesContent":["import { endOfToday } from 'date-fns/endOfToday'\nimport { format } from 'date-fns/format'\nimport { fromUnixTime } from 'date-fns/fromUnixTime'\nimport { getUnixTime } from 'date-fns/getUnixTime'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfToday } from 'date-fns/startOfToday'\n\nimport { TABLE_CONST } from '../constants/Table'\nimport {\n CategoryGuids,\n CategoryGuidToName,\n DATE_FORMATS_INTL,\n formatDate,\n ORDINAL_DATE_FORMAT,\n} from '../constants'\nimport { COLUMN_NAMES } from '../constants/Transaction'\nimport { TransactionStatus, TransactionType } from '../types'\nimport type { Category, DetailedAccount, Tag, Tagging, Transaction } from '../types'\n\nimport { type TransactionFilter } from '../stores/TransactionStore'\n\nimport { fromUnixUTC } from './DateUtil'\nimport { formatCurrency } from './NumberFormatting'\n\ninterface AccountsMap {\n [key: string]: DetailedAccount\n}\n\n// True for account-to-account transfers (top-level Transfer category). Used to\n// swap the payee/description for the institution name in the transaction detail\n// header, the table Payee column, and the mobile list.\nexport const isTransferTransaction = (\n transaction: Pick<Transaction, 'top_level_category_guid'>,\n): boolean => transaction.top_level_category_guid === CategoryGuids.TRANSFER\n\ninterface CategoriesMap {\n [key: string]: Category\n}\n\ninterface TaggingsMap {\n [key: string]: string[]\n}\n\nexport const buildDetailedTransactions = (\n transactions: Transaction[],\n accounts: DetailedAccount[],\n categories: Category[],\n taggings: Tagging[] = [],\n) => {\n const accountsMap = accounts.reduce((acc: AccountsMap, account: DetailedAccount) => {\n return { ...acc, [account.guid]: account }\n }, {})\n\n const categoriesMap = categories.reduce((acc: CategoriesMap, category: Category) => {\n return { ...acc, [category.guid]: category }\n }, {})\n\n const taggingsMap = taggings.reduce((acc: TaggingsMap, tagging: Tagging) => {\n const { transaction_guid, tag_guid } = tagging\n\n if (!acc[transaction_guid]) {\n return {\n ...acc,\n [transaction_guid]: [tag_guid],\n }\n }\n const tags = [...acc[transaction_guid], tag_guid] as string[]\n\n return {\n ...acc,\n [transaction_guid]: tags,\n }\n }, {})\n\n return transactions.map((transaction: Transaction) => {\n const account = accountsMap[transaction.account_guid]\n const category = transaction.category_guid ? categoriesMap[transaction.category_guid] : null\n const tags = taggingsMap[transaction.guid] ?? []\n\n return {\n ...transaction,\n account: account?.user_name || 'Unknown',\n accountIsClosed: account?.is_closed,\n accountIsHidden: account?.is_hidden,\n category: category?.name || 'Uncategorized',\n institutionName: account?.institution?.name || account?.institutionName || undefined,\n isIncome: transaction.top_level_category_guid === CategoryGuids.INCOME,\n isPending: transaction.feed_status === TransactionStatus.PENDING,\n number: account?.account_number || '',\n payee: transaction.description,\n tags,\n }\n })\n}\n\nconst UNCATEGORIZED_TERM = 'uncategorized'\n\nexport const sortAndFilterTransactions = (\n filter: TransactionFilter,\n expandedSplits: string[],\n sortColumn: string,\n sortDirection: string,\n tags: Tag[],\n transactions: Transaction[],\n) => {\n const filteredTransactions = filterTransactions(filter, tags, transactions)\n\n const sortedTransactions = sortTransactions(filteredTransactions, sortColumn, sortDirection)\n\n expandedSplits.forEach((guid) => {\n const index = sortedTransactions.findIndex((t) => t.guid === guid)\n const children = transactions.filter((t) => t.parent_guid === guid)\n sortedTransactions.splice(index + 1, 0, ...children)\n })\n return sortedTransactions\n}\n\nconst filterTransactions = (\n filter: TransactionFilter,\n tags: Tag[],\n transactions: Transaction[],\n) => {\n const {\n accounts = [],\n dateRange = { start: startOfMonth(startOfToday()), end: endOfToday() },\n custom = () => true,\n searchTerm: searchFilter = '',\n showSplits = false,\n } = filter\n\n return (\n transactions\n // Filters out parent of split transactions when showSplits is true and filters out split\n // transaction children when showSplits is false\n .filter((t) => (showSplits ? !t.has_been_split : !t.parent_guid))\n // Filter for selected accounts\n .filter((t) => Boolean(accounts.find((guid) => guid === t.account_guid)))\n // Filter for Date Range.\n // NOTE: bounds are local-midnight (getUnixTime of a local Date) while dates\n // are rendered/exported on the UTC calendar day (fromUnixUTC). For a\n // midnight-UTC row near a month boundary in a negative-offset browser the\n // two can disagree by a day. Left as-is here to keep this fix low-risk;\n // unifying range + search on UTC is tracked with the core-data ISO\n // migration (MEW-3961 follow-up).\n .filter((t) => t.date >= getUnixTime(dateRange.start) && t.date <= getUnixTime(dateRange.end))\n // Custom filter\n .filter(custom)\n // Search filter\n .filter((transaction) => {\n // Don't return uncategorized split transactions\n if (searchFilter === UNCATEGORIZED_TERM && transaction.has_been_split) {\n return false\n }\n\n // Split check - When searching for \"Splits\" and transaction is a split or is a parent of a split(s)\n const splitPattern = /^spl/i\n if (\n splitPattern.test(searchFilter) &&\n (transaction.has_been_split || Boolean(transaction.parent_guid))\n ) {\n return true\n }\n\n // Check transactions tags\n const isTagIncludedInSearch = transaction.tags.some((guid) =>\n isIncludedInSearch(searchFilter, tags.find((tag) => tag.guid === guid)?.name),\n )\n const date = fromUnixTime(transaction.date)\n return (\n isIncludedInSearch(searchFilter, transaction.payee) ||\n isIncludedInSearch(searchFilter, transaction.feed_description) ||\n isIncludedInSearch(searchFilter, transaction.category) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.MONTH)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.DAY)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.FULL_MONTH_DAY)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.MONTH_DAY_YEAR)) ||\n isIncludedInSearch(searchFilter, format(date, ORDINAL_DATE_FORMAT)) ||\n isIncludedInSearch(searchFilter, transaction.account) ||\n isIncludedInSearch(searchFilter, formatCurrency(transaction.amount, '0,0.00')) ||\n isIncludedInSearch(searchFilter, transaction.memo) ||\n isTagIncludedInSearch\n )\n })\n )\n}\n\nconst sortTransactions = (\n transactions: Transaction[],\n sortColumn: string,\n sortDirection: string,\n) => {\n return [...transactions].sort((a: Transaction, b: Transaction) => {\n const column = sortColumn as keyof Transaction\n const sortCompare = (a[column] || '') > (b[column] || '')\n\n let sortASC = sortCompare ? -1 : 1\n let sortDESC = sortCompare ? 1 : -1\n\n if (sortColumn === COLUMN_NAMES.DATE || sortColumn === COLUMN_NAMES.AMOUNT) {\n sortASC = sortCompare ? 1 : -1\n sortDESC = sortCompare ? -1 : 1\n }\n\n return sortDirection === TABLE_CONST.ASC ? sortASC : sortDESC\n })\n}\n\n/**\n * Determines if a value is included within the terms set in the search bar\n */\nconst isIncludedInSearch = (searchFilter: string, value: string | null | undefined) => {\n const lowerCasedTerms = searchFilter.toLowerCase().split(' ')\n const lowerCasedValue = value?.toString().toLowerCase() ?? ''\n\n return lowerCasedTerms.every((filter) => lowerCasedValue.indexOf(filter) > -1)\n}\n\n// TODO: Change the way formatting data is exported in order to handle localization\ntype FormattedTransaction = {\n Date: string\n Description: string\n 'Original Description': string\n Amount: string\n Type: string\n 'Parent Category': string\n Category: string\n Account: string\n Tags: string\n Memo: string\n Pending: string\n}\n\n/**\n * Collects the GUIDs of all transactions that act as parents\n * to one or more split (child) transactions.\n */\nfunction extractParentTransactionGuids(transactions: Transaction[]): Set<string> {\n return new Set(\n transactions.filter((tx) => tx.parent_guid !== undefined).map((tx) => tx.parent_guid!),\n )\n}\n\n/**\n * Organizes child transactions by their parent transaction GUID.\n * Returns a map from parent GUID to an array of its child transactions.\n */\nfunction mapChildTransactionsByParent(transactions: Transaction[]): Record<string, Transaction[]> {\n return transactions.reduce<Record<string, Transaction[]>>((accumulator, tx) => {\n if (tx.parent_guid) {\n if (!accumulator[tx.parent_guid]) accumulator[tx.parent_guid] = []\n accumulator[tx.parent_guid].push(tx)\n }\n return accumulator\n }, {})\n}\n\n/**\n * Produces a list of transactions based on the given sorted transactions,\n * where parent transactions that have split children are replaced by those\n * children sorted by descending absolute amount. All other transactions\n * are included as-is, with duplicates removed.\n */\nexport function flattenTransactionsWithSplits(\n sortedTransactions: Transaction[],\n allTransactions: Transaction[],\n): Transaction[] {\n // Set of parent transaction GUIDs that have split child transactions\n const parentTransactionGuidsWithSplits = extractParentTransactionGuids(allTransactions)\n\n // Map from parent GUID to array of its child split transactions\n const childTransactionsGroupedByParent = mapChildTransactionsByParent(allTransactions)\n\n // Track GUIDs of transactions already included in the result to avoid duplicates\n const processedTransactionGuids = new Set<string>()\n\n // Resulting list of transactions with parents replaced by their split children\n const flattenedTransactionList: Transaction[] = []\n\n for (const transaction of sortedTransactions) {\n if (parentTransactionGuidsWithSplits.has(transaction.guid)) {\n // This transaction is a parent with splits, so replace it with its children\n const splitChildTransactions = childTransactionsGroupedByParent[transaction.guid] ?? []\n\n splitChildTransactions\n .slice()\n .sort((a, b) => Math.abs(b.amount) - Math.abs(a.amount))\n .forEach((childTransaction) => {\n if (!processedTransactionGuids.has(childTransaction.guid)) {\n flattenedTransactionList.push(childTransaction)\n processedTransactionGuids.add(childTransaction.guid)\n }\n })\n } else if (!processedTransactionGuids.has(transaction.guid)) {\n // Normal transaction (no splits), add as-is if not already added\n flattenedTransactionList.push(transaction)\n processedTransactionGuids.add(transaction.guid)\n }\n }\n\n return flattenedTransactionList\n}\n\nexport const exportTransactionsToCSV = (transactions: Transaction[], tags: Tag[]) => {\n const csvContent = structureCSVData(formatTransactions(transactions, tags))\n\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })\n const link = document.createElement('a')\n link.href = URL.createObjectURL(blob)\n link.setAttribute('download', 'transactions.csv')\n\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n URL.revokeObjectURL(link.href)\n}\n\n/**\n * Formats a list of transactions into a list that will be used when\n * downloaded to a CSV file.\n */\nconst formatTransactions = (transactions: Transaction[], tags: Tag[]) => {\n return transactions.map((transaction) => ({\n // Post dates are calendar days; format on the UTC day so the export can't\n // shift to the prior day in a Western-offset browser.\n Date: formatDate(fromUnixUTC(transaction.date), DATE_FORMATS_INTL.YEAR_MONTH_DAY),\n Description: getDescription(transaction),\n 'Original Description': transaction.feed_description,\n Amount: `${transaction.amount}`,\n Type: transaction.transaction_type === TransactionType.CREDIT ? 'Credit' : 'Debit',\n 'Parent Category': CategoryGuidToName[transaction.top_level_category_guid || ''],\n Category: transaction.category,\n Account: transaction.account,\n Tags: getTags(transaction, tags),\n Memo: transaction.memo,\n Pending: transaction.isPending ? 'TRUE' : 'FALSE',\n })) as FormattedTransaction[]\n}\n\n/**\n * Escapes a value for safe CSV output\n */\nconst escapeCsvField = (field?: string | null): string => {\n if (!field) return ''\n return /[,\"\\n\\r]/.test(field) ? `\"${field.replace(/\"/g, '\"\"')}\"` : field\n}\n\n/**\n * Structures the CSV file data using the formatted transactions\n */\nconst structureCSVData = (formattedTransactions: FormattedTransaction[]) => {\n // Guard the empty export: Object.keys(undefined) would throw for a zero-row\n // list (e.g. an over-filtered range), so emit an empty file instead.\n if (formattedTransactions.length === 0) return ''\n\n const csvHeader = Object.keys(formattedTransactions[0]).join(',').concat('\\r\\n')\n const csvRows = formattedTransactions\n .map((transaction) => Object.values(transaction).map(escapeCsvField).join(','))\n .join('\\r\\n')\n\n return csvHeader + csvRows\n}\n\n/**\n * Get the description for a transaction that will be used within the CSV copy.\n * We want to highlight that split transactions are a split as well as\n * hidden transactions are hidden.\n */\nconst getDescription = (transaction: Transaction) => {\n let description = transaction.parent_guid ? 'Split: ' : ''\n description += transaction.is_hidden ? '(Excluded) ' : ''\n description += transaction.description\n\n return description\n}\n\nconst getTags = (transaction: Transaction, tags: Tag[]) => {\n return transaction.tags\n .map((tagGuid) => {\n const tag = tags.filter((t) => t.guid === tagGuid)[0]\n\n return tag?.name ?? ''\n })\n .join(' ')\n}\n"],"mappings":";;;;;;;;;;;;;AA+BA,IAAa,IAAA,CACX,MACY,EAAY,4BAA4B,EAAc,UAUvD,KAAA,CACX,GACA,GACA,GACA,IAAsB,CAAC,MACpB;AACH,QAAM,IAAc,EAAS,OAAA,CAAQ,GAAkB,OAC9C;AAAA,IAAE,GAAG;AAAA,IAAM,CAAA,EAAQ,IAAA,GAAO;AAAA,EAAQ,IACxC,CAAC,CAAC,GAEC,IAAgB,EAAW,OAAA,CAAQ,GAAoB,OACpD;AAAA,IAAE,GAAG;AAAA,IAAM,CAAA,EAAS,IAAA,GAAO;AAAA,EAAS,IAC1C,CAAC,CAAC,GAEC,IAAc,EAAS,OAAA,CAAQ,GAAkB,MAAqB;AAC1E,UAAM,EAAE,kBAAA,GAAkB,UAAA,EAAA,IAAa;AAEvC,QAAI,CAAC,EAAI,CAAA,EACP,QAAO;AAAA,MACL,GAAG;AAAA,MACF,CAAA,CAAA,GAAmB,CAAC,CAAQ;AAAA,IAC/B;AAEF,UAAM,IAAO,CAAC,GAAG,EAAI,CAAA,GAAmB,CAAQ;AAEhD,WAAO;AAAA,MACL,GAAG;AAAA,MACF,CAAA,CAAA,GAAmB;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAa,IAAA,CAAK,MAA6B;AACpD,UAAM,IAAU,EAAY,EAAY,YAAA,GAClC,IAAW,EAAY,gBAAgB,EAAc,EAAY,aAAA,IAAiB,MAClF,IAAO,EAAY,EAAY,IAAA,KAAS,CAAC;AAE/C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,GAAS,aAAa;AAAA,MAC/B,iBAAiB,GAAS;AAAA,MAC1B,iBAAiB,GAAS;AAAA,MAC1B,UAAU,GAAU,QAAQ;AAAA,MAC5B,iBAAiB,GAAS,aAAa,QAAQ,GAAS,mBAAmB;AAAA,MAC3E,UAAU,EAAY,4BAA4B,EAAc;AAAA,MAChE,WAAW,EAAY,gBAAgB,EAAkB;AAAA,MACzD,QAAQ,GAAS,kBAAkB;AAAA,MACnC,OAAO,EAAY;AAAA,MACnB,MAAA;AAAA,IACF;AAAA,EACF,CAAC;AACH,GAEM,IAAqB,iBAEd,KAAA,CACX,GACA,GACA,GACA,GACA,GACA,MACG;AACH,QAAM,IAAuB,EAAmB,GAAQ,GAAM,CAAY,GAEpE,IAAqB,EAAiB,GAAsB,GAAY,CAAa;AAE3F,SAAA,EAAe,QAAA,CAAS,MAAS;AAC/B,UAAM,IAAQ,EAAmB,UAAA,CAAW,MAAM,EAAE,SAAS,CAAI,GAC3D,IAAW,EAAa,OAAA,CAAQ,MAAM,EAAE,gBAAgB,CAAI;AAClE,IAAA,EAAmB,OAAO,IAAQ,GAAG,GAAG,GAAG,CAAQ;AAAA,EACrD,CAAC,GACM;AACT,GAEM,IAAA,CACJ,GACA,GACA,MACG;AACH,QAAM,EACJ,UAAA,IAAW,CAAC,GACZ,WAAA,IAAY;AAAA,IAAE,OAAO,EAAa,EAAa,CAAC;AAAA,IAAG,KAAK,EAAW;AAAA,EAAE,GACrE,QAAA,IAAA,MAAe,IACf,YAAY,IAAe,IAC3B,YAAA,IAAa,GAAA,IACX;AAEJ,SACE,EAGG,OAAA,CAAQ,MAAO,IAAa,CAAC,EAAE,iBAAiB,CAAC,EAAE,WAAY,EAE/D,OAAA,CAAQ,MAAM,EAAQ,EAAS,KAAA,CAAM,MAAS,MAAS,EAAE,YAAY,CAAE,EAQvE,OAAA,CAAQ,MAAM,EAAE,QAAQ,EAAY,EAAU,KAAK,KAAK,EAAE,QAAQ,EAAY,EAAU,GAAG,CAAC,EAE5F,OAAO,CAAM,EAEb,OAAA,CAAQ,MAAgB;AAEvB,QAAI,MAAiB,KAAsB,EAAY,eACrD,QAAO;AAKT,QACE,QAAa,KAAK,CAAY,MAC7B,EAAY,kBAA0B,EAAY,aAEnD,QAAO;AAIT,UAAM,IAAwB,EAAY,KAAK,KAAA,CAAM,MACnD,EAAmB,GAAc,EAAK,KAAA,CAAM,MAAQ,EAAI,SAAS,CAAI,GAAG,IAAI,CAC9E,GACM,IAAO,EAAa,EAAY,IAAI;AAC1C,WACE,EAAmB,GAAc,EAAY,KAAK,KAClD,EAAmB,GAAc,EAAY,gBAAgB,KAC7D,EAAmB,GAAc,EAAY,QAAQ,KACrD,EAAmB,GAAc,EAAW,GAAM,EAAkB,KAAK,CAAC,KAC1E,EAAmB,GAAc,EAAW,GAAM,EAAkB,GAAG,CAAC,KACxE,EAAmB,GAAc,EAAW,GAAM,EAAkB,cAAc,CAAC,KACnF,EAAmB,GAAc,EAAW,GAAM,EAAkB,cAAc,CAAC,KACnF,EAAmB,GAAc,EAAO,GAAA,IAAyB,CAAC,KAClE,EAAmB,GAAc,EAAY,OAAO,KACpD,EAAmB,GAAc,EAAe,EAAY,QAAQ,QAAQ,CAAC,KAC7E,EAAmB,GAAc,EAAY,IAAI,KACjD;AAAA,EAEJ,CAAC;AAEP,GAEM,IAAA,CACJ,GACA,GACA,MAEO,CAAC,GAAG,CAAY,EAAE,KAAA,CAAM,GAAgB,MAAmB;AAChE,QAAM,IAAS,GACT,KAAe,EAAE,CAAA,KAAW,OAAO,EAAE,CAAA,KAAW;AAEtD,MAAI,IAAU,IAAc,KAAK,GAC7B,IAAW,IAAc,IAAI;AAEjC,UAAI,MAAe,EAAa,QAAQ,MAAe,EAAa,YAClE,IAAU,IAAc,IAAI,IAC5B,IAAW,IAAc,KAAK,IAGzB,MAAkB,EAAY,MAAM,IAAU;AACvD,CAAC,GAMG,IAAA,CAAsB,GAAsB,MAAqC;AACrF,QAAM,IAAkB,EAAa,YAAY,EAAE,MAAM,GAAG,GACtD,IAAkB,GAAO,SAAS,EAAE,YAAY,KAAK;AAE3D,SAAO,EAAgB,MAAA,CAAO,MAAW,EAAgB,QAAQ,CAAM,IAAI,EAAE;AAC/E;AAqBA,SAAS,EAA8B,GAA0C;AAC/E,SAAO,IAAI,IACT,EAAa,OAAA,CAAQ,MAAO,EAAG,gBAAgB,MAAS,EAAE,IAAA,CAAK,MAAO,EAAG,WAAY,CACvF;AACF;AAMA,SAAS,EAA6B,GAA4D;AAChG,SAAO,EAAa,OAAA,CAAuC,GAAa,OAClE,EAAG,gBACA,EAAY,EAAG,WAAA,MAAc,EAAY,EAAG,WAAA,IAAe,CAAC,IACjE,EAAY,EAAG,WAAA,EAAa,KAAK,CAAE,IAE9B,IACN,CAAC,CAAC;AACP;AAQA,SAAgB,GACd,GACA,GACe;AAEf,QAAM,IAAmC,EAA8B,CAAe,GAGhF,IAAmC,EAA6B,CAAe,GAG/E,IAA4B,oBAAI,IAAY,GAG5C,IAA0C,CAAC;AAEjD,aAAW,KAAe,EACxB,CAAI,EAAiC,IAAI,EAAY,IAAI,KAExB,EAAiC,EAAY,IAAA,KAAS,CAAC,GAGnF,MAAM,EACN,KAAA,CAAM,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC,EACtD,QAAA,CAAS,MAAqB;AAC7B,IAAK,EAA0B,IAAI,EAAiB,IAAI,MACtD,EAAyB,KAAK,CAAgB,GAC9C,EAA0B,IAAI,EAAiB,IAAI;AAAA,EAEvD,CAAC,IACO,EAA0B,IAAI,EAAY,IAAI,MAExD,EAAyB,KAAK,CAAW,GACzC,EAA0B,IAAI,EAAY,IAAI;AAIlD,SAAO;AACT;AAEA,IAAa,KAAA,CAA2B,GAA6B,MAAgB;AACnF,QAAM,IAAa,EAAiB,EAAmB,GAAc,CAAI,CAAC,GAEpE,IAAO,IAAI,KAAK,CAAC,CAAU,GAAG,EAAE,MAAM,0BAA0B,CAAC,GACjE,IAAO,SAAS,cAAc,GAAG;AACvC,EAAA,EAAK,OAAO,IAAI,gBAAgB,CAAI,GACpC,EAAK,aAAa,YAAY,kBAAkB,GAEhD,SAAS,KAAK,YAAY,CAAI,GAC9B,EAAK,MAAM,GACX,SAAS,KAAK,YAAY,CAAI,GAE9B,IAAI,gBAAgB,EAAK,IAAI;AAC/B,GAMM,IAAA,CAAsB,GAA6B,MAChD,EAAa,IAAA,CAAK,OAAiB;AAAA,EAGxC,MAAM,EAAW,EAAY,EAAY,IAAI,GAAG,EAAkB,cAAc;AAAA,EAChF,aAAa,EAAe,CAAW;AAAA,EACvC,wBAAwB,EAAY;AAAA,EACpC,QAAQ,GAAG,EAAY,MAAA;AAAA,EACvB,MAAM,EAAY,qBAAqB,EAAgB,SAAS,WAAW;AAAA,EAC3E,mBAAmB,EAAmB,EAAY,2BAA2B,EAAA;AAAA,EAC7E,UAAU,EAAY;AAAA,EACtB,SAAS,EAAY;AAAA,EACrB,MAAM,EAAQ,GAAa,CAAI;AAAA,EAC/B,MAAM,EAAY;AAAA,EAClB,SAAS,EAAY,YAAY,SAAS;AAC5C,EAAE,GAME,IAAA,CAAkB,MACjB,IACE,WAAW,KAAK,CAAK,IAAI,IAAI,EAAM,QAAQ,MAAM,IAAI,CAAA,MAAO,IADhD,IAOf,IAAA,CAAoB,MAGpB,EAAsB,WAAW,IAAU,KAE7B,OAAO,KAAK,EAAsB,CAAA,CAAE,EAAE,KAAK,GAAG,EAAE,OAAO;AAAA,CAKlE,IAJS,EACb,IAAA,CAAK,MAAgB,OAAO,OAAO,CAAW,EAAE,IAAI,CAAc,EAAE,KAAK,GAAG,CAAC,EAC7E,KAAK;AAAA,CAEW,GAQf,IAAA,CAAkB,MAA6B;AACnD,MAAI,IAAc,EAAY,cAAc,YAAY;AACxD,SAAA,KAAe,EAAY,YAAY,gBAAgB,IACvD,KAAe,EAAY,aAEpB;AACT,GAEM,IAAA,CAAW,GAA0B,MAClC,EAAY,KAChB,IAAA,CAAK,MACQ,EAAK,OAAA,CAAQ,MAAM,EAAE,SAAS,CAAO,EAAE,CAAA,GAEvC,QAAQ,EACrB,EACA,KAAK,GAAG"}
|
|
1
|
+
{"version":3,"file":"TransactionUtils.es.js","names":[],"sources":["../../../src/core/utils/TransactionUtils.ts"],"sourcesContent":["import { endOfToday } from 'date-fns/endOfToday'\nimport { format } from 'date-fns/format'\nimport { fromUnixTime } from 'date-fns/fromUnixTime'\nimport { getUnixTime } from 'date-fns/getUnixTime'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfToday } from 'date-fns/startOfToday'\n\nimport { TABLE_CONST } from '../constants/Table'\nimport {\n CategoryGuids,\n CategoryGuidToName,\n DATE_FORMATS_INTL,\n formatDate,\n ORDINAL_DATE_FORMAT,\n} from '../constants'\nimport { COLUMN_NAMES } from '../constants/Transaction'\nimport { TransactionStatus, TransactionType } from '../types'\nimport type { Category, CategoryCopy, DetailedAccount, Tag, Tagging, Transaction } from '../types'\n\nimport { type TransactionFilter } from '../stores/TransactionStore'\n\nimport { getLocalizedCategoryName } from './CategoryUtil'\nimport { fromUnixUTC } from './DateUtil'\nimport { formatCurrency } from './NumberFormatting'\n\ninterface AccountsMap {\n [key: string]: DetailedAccount\n}\n\n// True for account-to-account transfers (top-level Transfer category). Used to\n// swap the payee/description for the institution name in the transaction detail\n// header, the table Payee column, and the mobile list.\nexport const isTransferTransaction = (\n transaction: Pick<Transaction, 'top_level_category_guid'>,\n): boolean => transaction.top_level_category_guid === CategoryGuids.TRANSFER\n\ninterface CategoriesMap {\n [key: string]: Category\n}\n\ninterface TaggingsMap {\n [key: string]: string[]\n}\n\nexport const buildDetailedTransactions = (\n transactions: Transaction[],\n accounts: DetailedAccount[],\n categories: Category[],\n taggings: Tagging[] = [],\n categoryCopy: CategoryCopy,\n) => {\n const accountsMap = accounts.reduce((acc: AccountsMap, account: DetailedAccount) => {\n return { ...acc, [account.guid]: account }\n }, {})\n\n const categoriesMap = categories.reduce((acc: CategoriesMap, category: Category) => {\n return { ...acc, [category.guid]: category }\n }, {})\n\n const taggingsMap = taggings.reduce((acc: TaggingsMap, tagging: Tagging) => {\n const { transaction_guid, tag_guid } = tagging\n\n if (!acc[transaction_guid]) {\n return {\n ...acc,\n [transaction_guid]: [tag_guid],\n }\n }\n const tags = [...acc[transaction_guid], tag_guid] as string[]\n\n return {\n ...acc,\n [transaction_guid]: tags,\n }\n }, {})\n\n return transactions.map((transaction: Transaction) => {\n const account = accountsMap[transaction.account_guid]\n const category = transaction.category_guid ? categoriesMap[transaction.category_guid] : null\n const tags = taggingsMap[transaction.guid] ?? []\n\n return {\n ...transaction,\n account: account?.user_name || 'Unknown',\n accountIsClosed: account?.is_closed,\n accountIsHidden: account?.is_hidden,\n category: category ? getLocalizedCategoryName(category, categoryCopy) : 'Uncategorized',\n institutionName: account?.institution?.name || account?.institutionName || undefined,\n isIncome: transaction.top_level_category_guid === CategoryGuids.INCOME,\n isPending: transaction.feed_status === TransactionStatus.PENDING,\n number: account?.account_number || '',\n payee: transaction.description,\n tags,\n }\n })\n}\n\nconst UNCATEGORIZED_TERM = 'uncategorized'\n\nexport const sortAndFilterTransactions = (\n filter: TransactionFilter,\n expandedSplits: string[],\n sortColumn: string,\n sortDirection: string,\n tags: Tag[],\n transactions: Transaction[],\n) => {\n const filteredTransactions = filterTransactions(filter, tags, transactions)\n\n const sortedTransactions = sortTransactions(filteredTransactions, sortColumn, sortDirection)\n\n expandedSplits.forEach((guid) => {\n const index = sortedTransactions.findIndex((t) => t.guid === guid)\n const children = transactions.filter((t) => t.parent_guid === guid)\n sortedTransactions.splice(index + 1, 0, ...children)\n })\n return sortedTransactions\n}\n\nconst filterTransactions = (\n filter: TransactionFilter,\n tags: Tag[],\n transactions: Transaction[],\n) => {\n const {\n accounts = [],\n dateRange = { start: startOfMonth(startOfToday()), end: endOfToday() },\n custom = () => true,\n searchTerm: searchFilter = '',\n showSplits = false,\n } = filter\n\n return (\n transactions\n // Filters out parent of split transactions when showSplits is true and filters out split\n // transaction children when showSplits is false\n .filter((t) => (showSplits ? !t.has_been_split : !t.parent_guid))\n // Filter for selected accounts\n .filter((t) => Boolean(accounts.find((guid) => guid === t.account_guid)))\n // Filter for Date Range.\n // NOTE: bounds are local-midnight (getUnixTime of a local Date) while dates\n // are rendered/exported on the UTC calendar day (fromUnixUTC). For a\n // midnight-UTC row near a month boundary in a negative-offset browser the\n // two can disagree by a day. Left as-is here to keep this fix low-risk;\n // unifying range + search on UTC is tracked with the core-data ISO\n // migration (MEW-3961 follow-up).\n .filter((t) => t.date >= getUnixTime(dateRange.start) && t.date <= getUnixTime(dateRange.end))\n // Custom filter\n .filter(custom)\n // Search filter\n .filter((transaction) => {\n // Don't return uncategorized split transactions\n if (searchFilter === UNCATEGORIZED_TERM && transaction.has_been_split) {\n return false\n }\n\n // Split check - When searching for \"Splits\" and transaction is a split or is a parent of a split(s)\n const splitPattern = /^spl/i\n if (\n splitPattern.test(searchFilter) &&\n (transaction.has_been_split || Boolean(transaction.parent_guid))\n ) {\n return true\n }\n\n // Check transactions tags\n const isTagIncludedInSearch = transaction.tags.some((guid) =>\n isIncludedInSearch(searchFilter, tags.find((tag) => tag.guid === guid)?.name),\n )\n const date = fromUnixTime(transaction.date)\n return (\n isIncludedInSearch(searchFilter, transaction.payee) ||\n isIncludedInSearch(searchFilter, transaction.feed_description) ||\n isIncludedInSearch(searchFilter, transaction.category) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.MONTH)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.DAY)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.FULL_MONTH_DAY)) ||\n isIncludedInSearch(searchFilter, formatDate(date, DATE_FORMATS_INTL.MONTH_DAY_YEAR)) ||\n isIncludedInSearch(searchFilter, format(date, ORDINAL_DATE_FORMAT)) ||\n isIncludedInSearch(searchFilter, transaction.account) ||\n isIncludedInSearch(searchFilter, formatCurrency(transaction.amount, '0,0.00')) ||\n isIncludedInSearch(searchFilter, transaction.memo) ||\n isTagIncludedInSearch\n )\n })\n )\n}\n\nconst sortTransactions = (\n transactions: Transaction[],\n sortColumn: string,\n sortDirection: string,\n) => {\n return [...transactions].sort((a: Transaction, b: Transaction) => {\n const column = sortColumn as keyof Transaction\n const sortCompare = (a[column] || '') > (b[column] || '')\n\n let sortASC = sortCompare ? -1 : 1\n let sortDESC = sortCompare ? 1 : -1\n\n if (sortColumn === COLUMN_NAMES.DATE || sortColumn === COLUMN_NAMES.AMOUNT) {\n sortASC = sortCompare ? 1 : -1\n sortDESC = sortCompare ? -1 : 1\n }\n\n return sortDirection === TABLE_CONST.ASC ? sortASC : sortDESC\n })\n}\n\n/**\n * Determines if a value is included within the terms set in the search bar\n */\nconst isIncludedInSearch = (searchFilter: string, value: string | null | undefined) => {\n const lowerCasedTerms = searchFilter.toLowerCase().split(' ')\n const lowerCasedValue = value?.toString().toLowerCase() ?? ''\n\n return lowerCasedTerms.every((filter) => lowerCasedValue.indexOf(filter) > -1)\n}\n\n// TODO: Change the way formatting data is exported in order to handle localization\ntype FormattedTransaction = {\n Date: string\n Description: string\n 'Original Description': string\n Amount: string\n Type: string\n 'Parent Category': string\n Category: string\n Account: string\n Tags: string\n Memo: string\n Pending: string\n}\n\n/**\n * Collects the GUIDs of all transactions that act as parents\n * to one or more split (child) transactions.\n */\nfunction extractParentTransactionGuids(transactions: Transaction[]): Set<string> {\n return new Set(\n transactions.filter((tx) => tx.parent_guid !== undefined).map((tx) => tx.parent_guid!),\n )\n}\n\n/**\n * Organizes child transactions by their parent transaction GUID.\n * Returns a map from parent GUID to an array of its child transactions.\n */\nfunction mapChildTransactionsByParent(transactions: Transaction[]): Record<string, Transaction[]> {\n return transactions.reduce<Record<string, Transaction[]>>((accumulator, tx) => {\n if (tx.parent_guid) {\n if (!accumulator[tx.parent_guid]) accumulator[tx.parent_guid] = []\n accumulator[tx.parent_guid].push(tx)\n }\n return accumulator\n }, {})\n}\n\n/**\n * Produces a list of transactions based on the given sorted transactions,\n * where parent transactions that have split children are replaced by those\n * children sorted by descending absolute amount. All other transactions\n * are included as-is, with duplicates removed.\n */\nexport function flattenTransactionsWithSplits(\n sortedTransactions: Transaction[],\n allTransactions: Transaction[],\n): Transaction[] {\n // Set of parent transaction GUIDs that have split child transactions\n const parentTransactionGuidsWithSplits = extractParentTransactionGuids(allTransactions)\n\n // Map from parent GUID to array of its child split transactions\n const childTransactionsGroupedByParent = mapChildTransactionsByParent(allTransactions)\n\n // Track GUIDs of transactions already included in the result to avoid duplicates\n const processedTransactionGuids = new Set<string>()\n\n // Resulting list of transactions with parents replaced by their split children\n const flattenedTransactionList: Transaction[] = []\n\n for (const transaction of sortedTransactions) {\n if (parentTransactionGuidsWithSplits.has(transaction.guid)) {\n // This transaction is a parent with splits, so replace it with its children\n const splitChildTransactions = childTransactionsGroupedByParent[transaction.guid] ?? []\n\n splitChildTransactions\n .slice()\n .sort((a, b) => Math.abs(b.amount) - Math.abs(a.amount))\n .forEach((childTransaction) => {\n if (!processedTransactionGuids.has(childTransaction.guid)) {\n flattenedTransactionList.push(childTransaction)\n processedTransactionGuids.add(childTransaction.guid)\n }\n })\n } else if (!processedTransactionGuids.has(transaction.guid)) {\n // Normal transaction (no splits), add as-is if not already added\n flattenedTransactionList.push(transaction)\n processedTransactionGuids.add(transaction.guid)\n }\n }\n\n return flattenedTransactionList\n}\n\nexport const exportTransactionsToCSV = (transactions: Transaction[], tags: Tag[]) => {\n const csvContent = structureCSVData(formatTransactions(transactions, tags))\n\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })\n const link = document.createElement('a')\n link.href = URL.createObjectURL(blob)\n link.setAttribute('download', 'transactions.csv')\n\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n URL.revokeObjectURL(link.href)\n}\n\n/**\n * Formats a list of transactions into a list that will be used when\n * downloaded to a CSV file.\n */\nconst formatTransactions = (transactions: Transaction[], tags: Tag[]) => {\n return transactions.map((transaction) => ({\n // Post dates are calendar days; format on the UTC day so the export can't\n // shift to the prior day in a Western-offset browser.\n Date: formatDate(fromUnixUTC(transaction.date), DATE_FORMATS_INTL.YEAR_MONTH_DAY),\n Description: getDescription(transaction),\n 'Original Description': transaction.feed_description,\n Amount: `${transaction.amount}`,\n Type: transaction.transaction_type === TransactionType.CREDIT ? 'Credit' : 'Debit',\n 'Parent Category': CategoryGuidToName[transaction.top_level_category_guid || ''],\n Category: transaction.category,\n Account: transaction.account,\n Tags: getTags(transaction, tags),\n Memo: transaction.memo,\n Pending: transaction.isPending ? 'TRUE' : 'FALSE',\n })) as FormattedTransaction[]\n}\n\n/**\n * Escapes a value for safe CSV output\n */\nconst escapeCsvField = (field?: string | null): string => {\n if (!field) return ''\n return /[,\"\\n\\r]/.test(field) ? `\"${field.replace(/\"/g, '\"\"')}\"` : field\n}\n\n/**\n * Structures the CSV file data using the formatted transactions\n */\nconst structureCSVData = (formattedTransactions: FormattedTransaction[]) => {\n // Guard the empty export: Object.keys(undefined) would throw for a zero-row\n // list (e.g. an over-filtered range), so emit an empty file instead.\n if (formattedTransactions.length === 0) return ''\n\n const csvHeader = Object.keys(formattedTransactions[0]).join(',').concat('\\r\\n')\n const csvRows = formattedTransactions\n .map((transaction) => Object.values(transaction).map(escapeCsvField).join(','))\n .join('\\r\\n')\n\n return csvHeader + csvRows\n}\n\n/**\n * Get the description for a transaction that will be used within the CSV copy.\n * We want to highlight that split transactions are a split as well as\n * hidden transactions are hidden.\n */\nconst getDescription = (transaction: Transaction) => {\n let description = transaction.parent_guid ? 'Split: ' : ''\n description += transaction.is_hidden ? '(Excluded) ' : ''\n description += transaction.description\n\n return description\n}\n\nconst getTags = (transaction: Transaction, tags: Tag[]) => {\n return transaction.tags\n .map((tagGuid) => {\n const tag = tags.filter((t) => t.guid === tagGuid)[0]\n\n return tag?.name ?? ''\n })\n .join(' ')\n}\n"],"mappings":"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"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { InstitutionLogo as
|
|
4
|
-
import { jsx as
|
|
5
|
-
var
|
|
6
|
-
const t = r.row.account, e = a[t.account_type];
|
|
7
|
-
return /* @__PURE__ */
|
|
1
|
+
import { useGlobalCopyStore as u } from "../../../common/context/hooks.es.js";
|
|
2
|
+
import n from "@mui/material/Stack";
|
|
3
|
+
import { InstitutionLogo as s, Text as c } from "@mxenabled/mxui";
|
|
4
|
+
import { jsx as o, jsxs as i } from "react/jsx-runtime";
|
|
5
|
+
var f = (r) => {
|
|
6
|
+
const { accounts: a } = u(), t = r.row.account, e = a.account_types[t.account_type];
|
|
7
|
+
return /* @__PURE__ */ i(n, {
|
|
8
8
|
alignItems: "center",
|
|
9
9
|
flexDirection: "row",
|
|
10
|
-
children: [/* @__PURE__ */
|
|
10
|
+
children: [/* @__PURE__ */ o(s, {
|
|
11
11
|
alt: `${t.institutionName}`,
|
|
12
12
|
institutionGuid: t.institution_guid || ""
|
|
13
|
-
}), /* @__PURE__ */
|
|
13
|
+
}), /* @__PURE__ */ i(n, {
|
|
14
14
|
ml: 12,
|
|
15
|
-
children: [/* @__PURE__ */
|
|
15
|
+
children: [/* @__PURE__ */ o(c, {
|
|
16
16
|
variant: "body2",
|
|
17
17
|
children: t.name
|
|
18
|
-
}), /* @__PURE__ */
|
|
18
|
+
}), /* @__PURE__ */ o(c, {
|
|
19
19
|
variant: "caption",
|
|
20
20
|
children: e
|
|
21
21
|
})]
|
|
@@ -23,8 +23,8 @@ var d = (r) => {
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
export {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
f as AccountCell,
|
|
27
|
+
f as default
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
//# sourceMappingURL=AccountCell.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountCell.es.js","names":[],"sources":["../../../../src/debts/components/debtstable/AccountCell.tsx"],"sourcesContent":["import { GridCellParams } from '@mui/x-data-grid-pro'\nimport { InstitutionLogo, Text } from '@mxenabled/mxui'\nimport Stack from '@mui/material/Stack'\n\nimport {
|
|
1
|
+
{"version":3,"file":"AccountCell.es.js","names":[],"sources":["../../../../src/debts/components/debtstable/AccountCell.tsx"],"sourcesContent":["import { GridCellParams } from '@mui/x-data-grid-pro'\nimport { InstitutionLogo, Text } from '@mxenabled/mxui'\nimport Stack from '@mui/material/Stack'\n\nimport { type Account } from '../../../core'\nimport { useGlobalCopyStore } from '../../../common'\n\nexport const AccountCell = (params: GridCellParams) => {\n const { accounts: accountsCopy } = useGlobalCopyStore()\n const account = params.row.account as Account\n const accountType = accountsCopy.account_types[account.account_type]\n\n return (\n <Stack alignItems=\"center\" flexDirection=\"row\">\n <InstitutionLogo\n alt={`${account.institutionName}`}\n institutionGuid={account.institution_guid || ''}\n />\n <Stack ml={12}>\n <Text variant=\"body2\">{account.name}</Text>\n <Text variant=\"caption\">{accountType}</Text>\n </Stack>\n </Stack>\n )\n}\n\nexport default AccountCell\n"],"mappings":";;;;AAOA,IAAa,IAAA,CAAe,MAA2B;AACrD,QAAM,EAAE,UAAU,EAAA,IAAiB,EAAmB,GAChD,IAAU,EAAO,IAAI,SACrB,IAAc,EAAa,cAAc,EAAQ,YAAA;AAEvD,SACE,gBAAA,EAAC,GAAD;AAAA,IAAO,YAAW;AAAA,IAAS,eAAc;AAAA,IAAzC,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,MACE,KAAK,GAAG,EAAQ,eAAA;AAAA,MAChB,iBAAiB,EAAQ,oBAAoB;AAAA,IAC9C,CAAA,GACD,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI;AAAA,MAAX,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,QAAM,SAAQ;AAAA,QAAS,UAAA,EAAQ;AAAA,MAAW,CAAA,GAC1C,gBAAA,EAAC,GAAD;AAAA,QAAM,SAAQ;AAAA,QAAW,UAAA;AAAA,MAAkB,CAAA,CACtC;AAAA,IACF,CAAA,CAAA;AAAA;AAEX"}
|
|
@@ -1,38 +1,42 @@
|
|
|
1
|
-
import { useAccountStore as
|
|
2
|
-
import { formatCurrency as
|
|
3
|
-
import { EmptyState as
|
|
4
|
-
import
|
|
5
|
-
import { getSpendingData as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
1
|
+
import { useAccountStore as I, useCategoryStore as M, useFinstrongStore as z, useGlobalCopyStore as L, useGlobalStore as T } from "../../../common/context/hooks.es.js";
|
|
2
|
+
import { formatCurrency as f } from "../../../core/utils/NumberFormatting.es.js";
|
|
3
|
+
import { EmptyState as k } from "../../../common/components/EmptyState.es.js";
|
|
4
|
+
import R from "../shared/DoubleBarChart.es.js";
|
|
5
|
+
import { getSpendingData as j } from "../../../spending/utils/SpendingData.es.js";
|
|
6
|
+
import F from "../../../spending/components/SpendingDonut.es.js";
|
|
7
|
+
import H from "../../../spending/components/SpendingLegend.es.js";
|
|
8
8
|
import o from "react";
|
|
9
|
-
import { observer as
|
|
9
|
+
import { observer as B } from "mobx-react-lite";
|
|
10
10
|
import d from "@mui/material/Stack";
|
|
11
|
-
import { H3 as
|
|
12
|
-
import { endOfMonth as
|
|
13
|
-
import { startOfMonth as
|
|
14
|
-
import
|
|
15
|
-
import { ChevronRight as
|
|
11
|
+
import { H3 as E } from "@mxenabled/mxui";
|
|
12
|
+
import { endOfMonth as G } from "date-fns/endOfMonth";
|
|
13
|
+
import { startOfMonth as O } from "date-fns/startOfMonth";
|
|
14
|
+
import Y from "@mui/material/Button";
|
|
15
|
+
import { ChevronRight as A } from "@mxenabled/mx-icons";
|
|
16
16
|
import { jsx as t, jsxs as a } from "react/jsx-runtime";
|
|
17
|
-
import { useTheme as
|
|
18
|
-
import
|
|
19
|
-
var
|
|
20
|
-
const { accounts: p } =
|
|
17
|
+
import { useTheme as P } from "@mui/material/styles";
|
|
18
|
+
import W from "@mui/material/Card";
|
|
19
|
+
var q = () => {
|
|
20
|
+
const { accounts: p } = I(), { categories: _, dateRangeCategoryTotals: l, loadDateRangeCategoryTotals: S } = M(), { monthlySummaries: r, handleFinstrongWidgetPrimaryCtaClick: m } = z(), { appDataStore: c } = T(), { categories: g, finstrong: e, goals: b, spending: x } = L(), w = P(), [s, C] = o.useState("");
|
|
21
21
|
o.useEffect(() => {
|
|
22
|
-
|
|
22
|
+
S(p, O(/* @__PURE__ */ new Date()), G(/* @__PURE__ */ new Date())).finally();
|
|
23
23
|
}, [p]);
|
|
24
|
-
const i = o.useMemo(() =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const i = o.useMemo(() => j(_, b, g, w, l), [
|
|
25
|
+
_,
|
|
26
|
+
g,
|
|
27
|
+
l
|
|
28
|
+
]), u = (n) => {
|
|
29
|
+
C(s === n ? "" : n);
|
|
30
|
+
}, { totalSpending: h, totalIncome: y } = o.useMemo(() => r?.reduce((n, { standard_spend: D = 0, income: v = 0 }) => ({
|
|
31
|
+
totalSpending: n.totalSpending + D,
|
|
32
|
+
totalIncome: n.totalIncome + v
|
|
29
33
|
}), {
|
|
30
34
|
totalSpending: 0,
|
|
31
35
|
totalIncome: 0
|
|
32
36
|
}), [r]);
|
|
33
37
|
return /* @__PURE__ */ a(d, {
|
|
34
38
|
sx: { gap: 24 },
|
|
35
|
-
children: [
|
|
39
|
+
children: [y > 0 || h > 0 ? /* @__PURE__ */ t(R, {
|
|
36
40
|
...c.appData.client_profile?.allow_trends_widget && { buttonLabel: e.key_indicators_spend.spend_less_than_you_make_drawer.view_more },
|
|
37
41
|
chartFor: "spending_vs_income",
|
|
38
42
|
data: r,
|
|
@@ -40,9 +44,9 @@ var W = () => {
|
|
|
40
44
|
legendLabelMain: e.key_indicators_spend.spend_less_than_you_make_drawer.spending,
|
|
41
45
|
onClick: () => m("trends")(),
|
|
42
46
|
title: e.key_indicators_spend.spend_less_than_you_make_drawer.trends,
|
|
43
|
-
totalComparison: y
|
|
44
|
-
totalMain:
|
|
45
|
-
}) : /* @__PURE__ */ t(
|
|
47
|
+
totalComparison: f(y, "0,0.00"),
|
|
48
|
+
totalMain: f(h, "0,0.00")
|
|
49
|
+
}) : /* @__PURE__ */ t(k, {
|
|
46
50
|
header: e.key_indicators_spend.spend_less_than_you_make_drawer.trends_zero_state_title,
|
|
47
51
|
icon: "multiline_chart",
|
|
48
52
|
subText: e.key_indicators_spend.spend_less_than_you_make_drawer.trends_zero_state_description,
|
|
@@ -58,7 +62,7 @@ var W = () => {
|
|
|
58
62
|
mt: 8
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
}), i.donutData.length > 0 ? /* @__PURE__ */ a(
|
|
65
|
+
}), i.donutData.length > 0 ? /* @__PURE__ */ a(W, {
|
|
62
66
|
sx: {
|
|
63
67
|
px: 16,
|
|
64
68
|
py: 24
|
|
@@ -73,8 +77,8 @@ var W = () => {
|
|
|
73
77
|
pt: 16,
|
|
74
78
|
px: 16
|
|
75
79
|
},
|
|
76
|
-
children: [/* @__PURE__ */ t(
|
|
77
|
-
endIcon: /* @__PURE__ */ t(
|
|
80
|
+
children: [/* @__PURE__ */ t(E, { children: e.key_indicators_spend.spend_less_than_you_make_drawer.spending }), c.appData.client_profile?.allow_spending_widget && /* @__PURE__ */ t(Y, {
|
|
81
|
+
endIcon: /* @__PURE__ */ t(A, {}),
|
|
78
82
|
onClick: () => m("spending")(),
|
|
79
83
|
size: "small",
|
|
80
84
|
sx: {
|
|
@@ -93,18 +97,18 @@ var W = () => {
|
|
|
93
97
|
pt: 16,
|
|
94
98
|
px: 24
|
|
95
99
|
},
|
|
96
|
-
children: [/* @__PURE__ */ t(
|
|
100
|
+
children: [/* @__PURE__ */ t(F, {
|
|
97
101
|
data: i,
|
|
98
102
|
onSelected: u,
|
|
99
103
|
selectedId: s,
|
|
100
|
-
totalLabel:
|
|
101
|
-
}), /* @__PURE__ */ t(
|
|
104
|
+
totalLabel: x.total_spending
|
|
105
|
+
}), /* @__PURE__ */ t(H, {
|
|
102
106
|
data: i,
|
|
103
107
|
onSelected: u,
|
|
104
108
|
selectedId: s
|
|
105
109
|
})]
|
|
106
110
|
})]
|
|
107
|
-
}) : /* @__PURE__ */ t(
|
|
111
|
+
}) : /* @__PURE__ */ t(k, {
|
|
108
112
|
header: e.key_indicators_spend.spend_less_than_you_make_drawer.spending_zero_state_title,
|
|
109
113
|
icon: "donut_small",
|
|
110
114
|
subText: e.key_indicators_spend.spend_less_than_you_make_drawer.spending_zero_state_description,
|
|
@@ -122,9 +126,9 @@ var W = () => {
|
|
|
122
126
|
}
|
|
123
127
|
})]
|
|
124
128
|
});
|
|
125
|
-
},
|
|
129
|
+
}, pe = B(q);
|
|
126
130
|
export {
|
|
127
|
-
|
|
131
|
+
pe as default
|
|
128
132
|
};
|
|
129
133
|
|
|
130
134
|
//# sourceMappingURL=SpendLessThanYouMakeComponent.es.js.map
|
package/dist/finstrong/components/SpendLessThanYouMake/SpendLessThanYouMakeComponent.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpendLessThanYouMakeComponent.es.js","names":[],"sources":["../../../../src/finstrong/components/SpendLessThanYouMake/SpendLessThanYouMakeComponent.tsx"],"sourcesContent":["import React from 'react'\nimport { observer } from 'mobx-react-lite'\n\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\nimport { useTheme } from '@mui/material/styles'\n\nimport { H3 } from '@mxenabled/mxui'\nimport { ChevronRight } from '@mxenabled/mx-icons'\n\nimport {\n useAccountStore,\n useCategoryStore,\n useFinstrongStore,\n useGlobalStore,\n useGlobalCopyStore,\n} from '../../../common'\nimport { EmptyState } from '../../../common/components/EmptyState'\nimport DoubleBarChart from '../shared/DoubleBarChart'\nimport { getSpendingData } from '../../../spending/utils/SpendingData'\nimport SpendingDonut from '../../../spending/components/SpendingDonut'\nimport SpendingLegend from '../../../spending/components/SpendingLegend'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\n\nconst SpendLessThanYouMakeComponent: React.FC = () => {\n const { accounts } = useAccountStore()\n const { categories, dateRangeCategoryTotals, loadDateRangeCategoryTotals } = useCategoryStore()\n const { monthlySummaries, handleFinstrongWidgetPrimaryCtaClick } = useFinstrongStore()\n const { appDataStore } = useGlobalStore()\n const { finstrong: copy
|
|
1
|
+
{"version":3,"file":"SpendLessThanYouMakeComponent.es.js","names":[],"sources":["../../../../src/finstrong/components/SpendLessThanYouMake/SpendLessThanYouMakeComponent.tsx"],"sourcesContent":["import React from 'react'\nimport { observer } from 'mobx-react-lite'\n\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\nimport { useTheme } from '@mui/material/styles'\n\nimport { H3 } from '@mxenabled/mxui'\nimport { ChevronRight } from '@mxenabled/mx-icons'\n\nimport {\n useAccountStore,\n useCategoryStore,\n useFinstrongStore,\n useGlobalStore,\n useGlobalCopyStore,\n} from '../../../common'\nimport { EmptyState } from '../../../common/components/EmptyState'\nimport DoubleBarChart from '../shared/DoubleBarChart'\nimport { getSpendingData } from '../../../spending/utils/SpendingData'\nimport SpendingDonut from '../../../spending/components/SpendingDonut'\nimport SpendingLegend from '../../../spending/components/SpendingLegend'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\n\nconst SpendLessThanYouMakeComponent: React.FC = () => {\n const { accounts } = useAccountStore()\n const { categories, dateRangeCategoryTotals, loadDateRangeCategoryTotals } = useCategoryStore()\n const { monthlySummaries, handleFinstrongWidgetPrimaryCtaClick } = useFinstrongStore()\n const { appDataStore } = useGlobalStore()\n const {\n categories: categoriesCopy,\n finstrong: copy,\n goals: goalsCopy,\n spending: spendingCopy,\n } = useGlobalCopyStore()\n const theme = useTheme()\n\n const [selectedId, setSelectedId] = React.useState('')\n\n // Load category data for spending chart\n React.useEffect(() => {\n loadDateRangeCategoryTotals(\n accounts,\n startOfMonth(new Date()),\n endOfMonth(new Date()),\n ).finally()\n }, [accounts])\n\n // Spending data for donut chart\n const spendingData = React.useMemo(\n () => getSpendingData(categories, goalsCopy, categoriesCopy, theme, dateRangeCategoryTotals),\n [categories, categoriesCopy, dateRangeCategoryTotals],\n )\n\n const handleSelected = (id: string) => {\n setSelectedId(selectedId === id ? '' : id)\n }\n\n const { totalSpending, totalIncome } = React.useMemo(() => {\n return monthlySummaries?.reduce(\n (\n totals: { totalSpending: number; totalIncome: number },\n { standard_spend = 0, income = 0 },\n ) => ({\n totalSpending: totals.totalSpending + standard_spend,\n totalIncome: totals.totalIncome + income,\n }),\n { totalSpending: 0, totalIncome: 0 },\n )\n }, [monthlySummaries])\n\n //TODO: Add Functionalities for View More Buttons\n return (\n <Stack sx={{ gap: 24 }}>\n {totalIncome > 0 || totalSpending > 0 ? (\n <DoubleBarChart\n {...(appDataStore.appData.client_profile?.allow_trends_widget && {\n buttonLabel: copy.key_indicators_spend.spend_less_than_you_make_drawer.view_more,\n })}\n chartFor=\"spending_vs_income\"\n data={monthlySummaries}\n legendLabelComparison={copy.key_indicators_spend.spend_less_than_you_make_drawer.income}\n legendLabelMain={copy.key_indicators_spend.spend_less_than_you_make_drawer.spending}\n onClick={() => handleFinstrongWidgetPrimaryCtaClick('trends')()}\n title={copy.key_indicators_spend.spend_less_than_you_make_drawer.trends}\n totalComparison={formatCurrency(totalIncome, '0,0.00')}\n totalMain={formatCurrency(totalSpending, '0,0.00')}\n />\n ) : (\n <EmptyState\n header={copy.key_indicators_spend.spend_less_than_you_make_drawer.trends_zero_state_title}\n icon=\"multiline_chart\"\n subText={\n copy.key_indicators_spend.spend_less_than_you_make_drawer.trends_zero_state_description\n }\n sx={{\n backgroundColor: 'background.paper',\n borderRadius: '8px',\n boxShadow: 1,\n p: 24,\n pb: 48,\n '.empty-state-subtext': {\n fontSize: 15,\n lineHeight: '24px',\n mt: 8,\n },\n }}\n />\n )}\n\n {/* Mini Spending Chart Component */}\n {spendingData.donutData.length > 0 ? (\n <Card\n sx={{\n px: 16,\n py: 24,\n }}\n >\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n gap: 12,\n justifyContent: 'space-between',\n pb: 8,\n pt: 16,\n px: 16,\n }}\n >\n <H3>{copy.key_indicators_spend.spend_less_than_you_make_drawer.spending}</H3>\n {appDataStore.appData.client_profile?.allow_spending_widget && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => handleFinstrongWidgetPrimaryCtaClick('spending')()}\n size=\"small\"\n sx={{\n pl: 8,\n height: 24,\n }}\n variant=\"text\"\n >\n {copy.key_indicators_spend.spend_less_than_you_make_drawer.view_more}\n </Button>\n )}\n </Stack>\n <Stack\n sx={{ flexDirection: 'row', gap: 12, justifyContent: 'center', pb: 32, pt: 16, px: 24 }}\n >\n <SpendingDonut\n data={spendingData}\n onSelected={handleSelected}\n selectedId={selectedId}\n totalLabel={spendingCopy.total_spending}\n />\n\n <SpendingLegend\n data={spendingData}\n onSelected={handleSelected}\n selectedId={selectedId}\n />\n </Stack>\n </Card>\n ) : (\n <EmptyState\n header={\n copy.key_indicators_spend.spend_less_than_you_make_drawer.spending_zero_state_title\n }\n icon=\"donut_small\"\n subText={\n copy.key_indicators_spend.spend_less_than_you_make_drawer\n .spending_zero_state_description\n }\n sx={{\n backgroundColor: 'background.paper',\n borderRadius: '8px',\n boxShadow: 1,\n p: 24,\n pb: 48,\n '.empty-state-subtext': {\n fontSize: 15,\n lineHeight: '24px',\n mt: 8,\n },\n }}\n />\n )}\n </Stack>\n )\n}\n\nexport default observer(SpendLessThanYouMakeComponent)\n"],"mappings":"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"}
|