@mx-cartographer/experiences 9.6.8 → 9.6.10
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 +8 -0
- package/dist/core/stores/AccountStore.d.ts +1 -0
- package/dist/core/stores/AccountStore.es.js +47 -42
- package/dist/core/stores/AccountStore.es.js.map +1 -1
- package/dist/core/stores/AppDataStore.d.ts +1 -0
- package/dist/core/stores/AppDataStore.es.js +15 -11
- package/dist/core/stores/AppDataStore.es.js.map +1 -1
- package/dist/core/stores/BeatStore.d.ts +1 -0
- package/dist/core/stores/BeatStore.es.js +10 -6
- package/dist/core/stores/BeatStore.es.js.map +1 -1
- package/dist/core/stores/BudgetsStore.d.ts +1 -0
- package/dist/core/stores/BudgetsStore.es.js +37 -35
- package/dist/core/stores/BudgetsStore.es.js.map +1 -1
- package/dist/core/stores/CategoryStore.d.ts +1 -0
- package/dist/core/stores/CategoryStore.es.js +36 -32
- package/dist/core/stores/CategoryStore.es.js.map +1 -1
- package/dist/core/stores/GlobalCopyStore.d.ts +1 -0
- package/dist/core/stores/GlobalCopyStore.es.js +17 -13
- package/dist/core/stores/GlobalCopyStore.es.js.map +1 -1
- package/dist/core/stores/HoldingStore.d.ts +1 -0
- package/dist/core/stores/HoldingStore.es.js +6 -2
- package/dist/core/stores/HoldingStore.es.js.map +1 -1
- package/dist/core/stores/TransactionStore.d.ts +1 -0
- package/dist/core/stores/TransactionStore.es.js +71 -67
- package/dist/core/stores/TransactionStore.es.js.map +1 -1
- package/dist/core/types/localization/InsightsFeedCopy.d.ts +1 -1
- package/dist/insights-v2/actionBlocks/MakeTransferActionBlock/layout.es.js +6 -6
- package/dist/insights-v2/actionBlocks/MakeTransferActionBlock/layout.es.js.map +1 -1
- package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.d.ts +2 -2
- package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.es.js +15 -15
- package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.es.js.map +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.d.ts +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.es.js +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.es.js.map +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.d.ts +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.es.js +16 -16
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.es.js.map +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/AccountBlock.es.js +6 -7
- package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/AccountBlock.es.js.map +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/layout.d.ts +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/layout.es.js +14 -30
- package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/layout.es.js.map +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.d.ts +8 -8
- package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.es.js +14 -14
- package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.es.js.map +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/UpcomingSubscriptionBlock/UpcomingSubscriptionBlock.es.js +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/UpcomingSubscriptionBlock/UpcomingSubscriptionBlock.es.js.map +1 -1
- package/dist/insights-v2/blocks/ComponentBlocks/UpcomingSubscriptionBlock/layout.d.ts +4 -3
- package/dist/insights-v2/blocks/ComponentBlocks/UpcomingSubscriptionBlock/layout.es.js +2 -2
- package/dist/insights-v2/blocks/ComponentBlocks/UpcomingSubscriptionBlock/layout.es.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { CategoryGuids as
|
|
2
|
-
import { BudgetsApi as
|
|
1
|
+
import { CategoryGuids as g } from "../constants/Category.es.js";
|
|
2
|
+
import { BudgetsApi as o } from "../api/BudgetsApi.es.js";
|
|
3
3
|
import { upsertByGuid as n } from "../utils/upsertByGuid.es.js";
|
|
4
|
-
import { augmentBudgets as c, findBudget as
|
|
5
|
-
import { endOfMonth as
|
|
4
|
+
import { augmentBudgets as c, findBudget as l, findSubBudget as B } from "../utils/BudgetUtil.es.js";
|
|
5
|
+
import { endOfMonth as p } from "date-fns/endOfMonth";
|
|
6
6
|
import { startOfMonth as f } from "date-fns/startOfMonth";
|
|
7
|
-
import { startOfToday as
|
|
8
|
-
import { makeAutoObservable as
|
|
9
|
-
var
|
|
10
|
-
api = new
|
|
7
|
+
import { startOfToday as h } from "date-fns/startOfToday";
|
|
8
|
+
import { makeAutoObservable as m, reaction as S, runInAction as u } from "mobx";
|
|
9
|
+
var D = class {
|
|
10
|
+
api = new o("/", "");
|
|
11
11
|
globalStore;
|
|
12
12
|
alert = null;
|
|
13
13
|
dateRange = {
|
|
14
|
-
start: f(
|
|
15
|
-
end:
|
|
14
|
+
start: f(h()),
|
|
15
|
+
end: p(h())
|
|
16
16
|
};
|
|
17
17
|
budgets = [];
|
|
18
18
|
selectedBudget;
|
|
@@ -21,23 +21,23 @@ var A = class {
|
|
|
21
21
|
theme = {};
|
|
22
22
|
merchantBudgets = [];
|
|
23
23
|
constructor(t) {
|
|
24
|
-
this.globalStore = t, this.api = new
|
|
24
|
+
this.globalStore = t, this.api = new o(t.endpoint, t.sessionToken, t.onError), m(this);
|
|
25
25
|
}
|
|
26
26
|
loadBudgetData = async () => {
|
|
27
|
-
this.globalStore.recurringTransactionsStore.isDataLoaded || await this.globalStore.recurringTransactionsStore.loadRepeatingTransactionData(), await this.loadBudgets(),
|
|
27
|
+
this.globalStore.recurringTransactionsStore.isDataLoaded || await this.globalStore.recurringTransactionsStore.loadRepeatingTransactionData(), await this.loadBudgets(), S(() => this.detailedBudgets, (t) => {
|
|
28
28
|
if (t) {
|
|
29
29
|
const e = this.selectedBudget;
|
|
30
|
-
e && (this.selectedBudget =
|
|
30
|
+
e && (this.selectedBudget = l(t, e.guid));
|
|
31
31
|
const r = this.selectedSubBudget;
|
|
32
|
-
r && r.parent_guid && (this.selectedSubBudget =
|
|
32
|
+
r && r.parent_guid && (this.selectedSubBudget = B(t, r.guid, r.parent_guid));
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
36
|
get detailedBudgets() {
|
|
37
|
-
return c(this.budgets.filter((t) => t.category_guid !==
|
|
37
|
+
return c(this.budgets.filter((t) => t.category_guid !== g.INCOME && t.category_guid !== g.TRANSFER && t.category_guid !== g.INVESTMENTS), this.globalStore.categoryStore.detailedCategories, this.globalStore.copyStore.copy.budgets, this.theme).sort((t, e) => t.percentage > e.percentage ? -1 : t.percentage < e.percentage ? 1 : 0);
|
|
38
38
|
}
|
|
39
39
|
get incomeBudget() {
|
|
40
|
-
return c(this.budgets.filter((t) => !t.parent_guid), this.globalStore.categoryStore.detailedCategories, this.globalStore.copyStore.copy.budgets, this.theme).reduce((t, e) => e.category_guid !==
|
|
40
|
+
return c(this.budgets.filter((t) => !t.parent_guid), this.globalStore.categoryStore.detailedCategories, this.globalStore.copyStore.copy.budgets, this.theme).reduce((t, e) => e.category_guid !== g.INCOME ? t : !t || e.percentage > t.percentage ? e : t, void 0)?.amount;
|
|
41
41
|
}
|
|
42
42
|
get unbudgetedCategories() {
|
|
43
43
|
return this.globalStore.categoryStore.spendCategories.filter((t) => !this.detailedBudgets.find((e) => e.category_guid === t.guid));
|
|
@@ -48,23 +48,20 @@ var A = class {
|
|
|
48
48
|
addBudget = async (t, e, r = null) => {
|
|
49
49
|
try {
|
|
50
50
|
const s = await this.api.addBudget(t, e, r);
|
|
51
|
-
return
|
|
51
|
+
return u(() => {
|
|
52
52
|
this.budgets = n(this.budgets, s);
|
|
53
53
|
}), !0;
|
|
54
54
|
} catch (s) {
|
|
55
|
-
return
|
|
55
|
+
return this.captureError("addBudget", s), !1;
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
deleteBudget = async (t) => {
|
|
59
|
-
const e = [t, ...this.budgets.filter((a) => a.parent_guid === t).map((a) => a.guid)], r = await Promise.allSettled(e.map((a) => this.api.deleteBudget(a))), s = new Set(e.filter((a,
|
|
60
|
-
|
|
59
|
+
const e = [t, ...this.budgets.filter((a) => a.parent_guid === t).map((a) => a.guid)], r = await Promise.allSettled(e.map((a) => this.api.deleteBudget(a))), s = new Set(e.filter((a, i) => r[i].status === "fulfilled"));
|
|
60
|
+
u(() => {
|
|
61
61
|
this.budgets = this.budgets.filter((a) => !s.has(a.guid));
|
|
62
62
|
});
|
|
63
63
|
let d = !1;
|
|
64
|
-
for (const [a,
|
|
65
|
-
store: "BudgetsStore",
|
|
66
|
-
guid: e[a]
|
|
67
|
-
}));
|
|
64
|
+
for (const [a, i] of r.entries()) i.status === "rejected" && (d = !0, this.captureError("deleteBudget", i.reason, { guid: e[a] }));
|
|
68
65
|
return !d;
|
|
69
66
|
};
|
|
70
67
|
loadBudgets = async () => {
|
|
@@ -72,7 +69,7 @@ var A = class {
|
|
|
72
69
|
const t = await this.api.getBudgets();
|
|
73
70
|
this.setBudgets(t);
|
|
74
71
|
} catch (t) {
|
|
75
|
-
throw
|
|
72
|
+
throw this.captureError("loadBudgets", t), t;
|
|
76
73
|
}
|
|
77
74
|
};
|
|
78
75
|
setAlert = (t) => this.alert = t;
|
|
@@ -99,7 +96,7 @@ var A = class {
|
|
|
99
96
|
isSuccess: !0
|
|
100
97
|
};
|
|
101
98
|
} catch (e) {
|
|
102
|
-
return
|
|
99
|
+
return this.captureError("recalculateBudgets", e), await this.loadBudgets().catch(() => {
|
|
103
100
|
}), {
|
|
104
101
|
data: [],
|
|
105
102
|
isSuccess: !1
|
|
@@ -109,12 +106,12 @@ var A = class {
|
|
|
109
106
|
updateBudget = async (t) => {
|
|
110
107
|
try {
|
|
111
108
|
const e = await this.api.updateBudget(t);
|
|
112
|
-
return
|
|
109
|
+
return u(() => {
|
|
113
110
|
const r = this.budgets.findIndex((s) => s.guid === t.guid);
|
|
114
111
|
r >= 0 && (this.budgets[r] = e);
|
|
115
112
|
}), !0;
|
|
116
113
|
} catch (e) {
|
|
117
|
-
return
|
|
114
|
+
return this.captureError("updateBudget", e), !1;
|
|
118
115
|
}
|
|
119
116
|
};
|
|
120
117
|
getMerchantBudgetByMerchantGuid = (t) => this.merchantBudgets.find((e) => e?.merchant_guid === t);
|
|
@@ -124,36 +121,41 @@ var A = class {
|
|
|
124
121
|
addMerchantBudget = async (t, e) => {
|
|
125
122
|
try {
|
|
126
123
|
const r = await this.api.addMerchantBudget(t, e);
|
|
127
|
-
return
|
|
124
|
+
return u(() => {
|
|
128
125
|
this.merchantBudgets = n(this.merchantBudgets, r);
|
|
129
126
|
}), { isSuccess: !0 };
|
|
130
127
|
} catch (r) {
|
|
131
|
-
return
|
|
128
|
+
return this.captureError("addMerchantBudget", r), { isSuccess: !1 };
|
|
132
129
|
}
|
|
133
130
|
};
|
|
134
131
|
updateMerchantBudget = async (t) => {
|
|
135
132
|
try {
|
|
136
133
|
const { guid: e, amount: r } = t, s = await this.api.updateMerchantBudget(e, r);
|
|
137
|
-
return
|
|
134
|
+
return u(() => {
|
|
138
135
|
const d = this.merchantBudgets.findIndex((a) => a.guid === t.guid);
|
|
139
136
|
d >= 0 && (this.merchantBudgets[d] = s);
|
|
140
137
|
}), { isSuccess: !0 };
|
|
141
138
|
} catch (e) {
|
|
142
|
-
return
|
|
139
|
+
return this.captureError("updateMerchantBudget", e), { isSuccess: !1 };
|
|
143
140
|
}
|
|
144
141
|
};
|
|
145
142
|
deleteMerchantBudget = async (t) => {
|
|
146
143
|
try {
|
|
147
|
-
return await this.api.deleteMerchantBudget(t),
|
|
144
|
+
return await this.api.deleteMerchantBudget(t), u(() => {
|
|
148
145
|
this.merchantBudgets = this.merchantBudgets.filter((e) => e.guid !== t);
|
|
149
146
|
}), { isSuccess: !0 };
|
|
150
147
|
} catch (e) {
|
|
151
|
-
return
|
|
148
|
+
return this.captureError("deleteMerchantBudget", e), { isSuccess: !1 };
|
|
152
149
|
}
|
|
153
150
|
};
|
|
151
|
+
captureError = (t, e, r) => this.globalStore.captureException(e, {
|
|
152
|
+
store: "BudgetsStore",
|
|
153
|
+
action: t,
|
|
154
|
+
...r
|
|
155
|
+
});
|
|
154
156
|
};
|
|
155
157
|
export {
|
|
156
|
-
|
|
158
|
+
D as BudgetsStore
|
|
157
159
|
};
|
|
158
160
|
|
|
159
161
|
//# sourceMappingURL=BudgetsStore.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BudgetsStore.es.js","names":[],"sources":["../../../src/core/stores/BudgetsStore.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from 'mobx'\n\nimport { endOfMonth } from 'date-fns/endOfMonth'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfToday } from 'date-fns/startOfToday'\n\nimport { Theme } from '@mui/system'\n\nimport { BudgetsApi } from '../api'\nimport { upsertByGuid } from '../utils/upsertByGuid'\nimport { CategoryGuids } from '../constants'\nimport { augmentBudgets, findBudget, findSubBudget } from '../utils/BudgetUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Budget,\n Category,\n DateRange,\n DetailedBudget,\n DetailedCategory,\n MerchantBudget,\n} from '../types'\n\nexport class BudgetsStore {\n api: BudgetsApi = new BudgetsApi('/', '')\n\n globalStore: GlobalStore\n\n alert: { msg: string; severity: 'success' | 'error' } | null = null\n dateRange: DateRange = { start: startOfMonth(startOfToday()), end: endOfMonth(startOfToday()) }\n budgets: Budget[] = []\n selectedBudget: DetailedBudget | undefined\n selectedSubBudget: DetailedBudget | undefined\n isDataLoaded = false\n theme = {} as Theme\n merchantBudgets: MerchantBudget[] = []\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.api = new BudgetsApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable(this)\n }\n\n loadBudgetData = async () => {\n if (!this.globalStore.recurringTransactionsStore.isDataLoaded) {\n await this.globalStore.recurringTransactionsStore.loadRepeatingTransactionData()\n }\n await this.loadBudgets()\n\n /** Reaction to update the selected budget/sub-budget if detailedBudgets is updated **/\n reaction(\n () => this.detailedBudgets,\n (detailedBudgets) => {\n if (detailedBudgets) {\n const budget = this.selectedBudget\n if (budget) {\n this.selectedBudget = findBudget(detailedBudgets, budget.guid)\n }\n\n const subBudget = this.selectedSubBudget\n if (subBudget && subBudget.parent_guid) {\n this.selectedSubBudget = findSubBudget(\n detailedBudgets,\n subBudget.guid,\n subBudget.parent_guid,\n )\n }\n }\n },\n )\n }\n\n get detailedBudgets(): DetailedBudget[] {\n return augmentBudgets(\n this.budgets.filter(\n (budget: Budget) =>\n budget.category_guid !== CategoryGuids.INCOME &&\n budget.category_guid !== CategoryGuids.TRANSFER &&\n budget.category_guid !== CategoryGuids.INVESTMENTS,\n ),\n this.globalStore.categoryStore.detailedCategories,\n this.globalStore.copyStore.copy.budgets,\n this.theme,\n ).sort((a, b) => {\n if (a.percentage > b.percentage) return -1\n if (a.percentage < b.percentage) return 1\n return 0\n })\n }\n\n // TODO: Replace this with income from recurring transactions. Income budgets are deprecated.\n get incomeBudget(): number | undefined {\n const filteredBudgets = augmentBudgets(\n this.budgets.filter((budget: Budget) => !budget.parent_guid),\n this.globalStore.categoryStore.detailedCategories,\n this.globalStore.copyStore.copy.budgets,\n this.theme,\n )\n\n const incomeBudget = filteredBudgets.reduce(\n (acc, curr) => {\n if (curr.category_guid !== CategoryGuids.INCOME) return acc\n if (!acc || curr.percentage > acc.percentage) return curr\n return acc\n },\n undefined as DetailedBudget | undefined,\n )\n\n return incomeBudget?.amount\n }\n\n get unbudgetedCategories(): DetailedCategory[] {\n return this.globalStore.categoryStore.spendCategories.filter(\n (category) => !this.detailedBudgets.find((budget) => budget.category_guid === category.guid),\n )\n }\n\n get totalBudgeted(): number {\n return this.detailedBudgets.reduce((total, budget) => total + budget.amount, 0)\n }\n\n /** Resolves `true` when the budget was created, `false` when the API call failed. */\n addBudget = async (\n category: Category,\n amount: number,\n parentGuid: string | null = null,\n ): Promise<boolean> => {\n try {\n const budget = await this.api.addBudget(category, amount, parentGuid)\n runInAction(() => {\n this.budgets = upsertByGuid(this.budgets, budget)\n })\n return true\n } catch (error) {\n console.error('Error adding budget:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n return false\n }\n }\n\n deleteBudget = async (guid: string): Promise<boolean> => {\n const guids = [guid, ...this.budgets.filter((b) => b.parent_guid === guid).map((b) => b.guid)]\n const results = await Promise.allSettled(guids.map((g) => this.api.deleteBudget(g)))\n const deleted = new Set(guids.filter((_, i) => results[i].status === 'fulfilled'))\n runInAction(() => {\n this.budgets = this.budgets.filter((b) => !deleted.has(b.guid))\n })\n let failed = false\n for (const [i, r] of results.entries()) {\n if (r.status === 'rejected') {\n failed = true\n console.error('Error deleting budget:', r.reason)\n this.globalStore.captureException(r.reason, { store: 'BudgetsStore', guid: guids[i] })\n }\n }\n return !failed\n }\n\n loadBudgets = async () => {\n try {\n const budgets = await this.api.getBudgets()\n this.setBudgets(budgets)\n } catch (error) {\n console.error('Error occurred while loading budgets:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n throw error\n }\n }\n\n setAlert = (alert: { msg: string; severity: 'success' | 'error' } | null) => (this.alert = alert)\n\n setBudgets = (budgets: Budget[]) => {\n this.budgets = budgets\n this.isDataLoaded = true\n }\n\n setDateRange = (dateRange: DateRange) => (this.dateRange = dateRange)\n\n setSelectedBudget = (budget: DetailedBudget | undefined) => (this.selectedBudget = budget)\n\n setSelectedSubBudget = (budget: DetailedBudget | undefined) => (this.selectedSubBudget = budget)\n\n setTheme = (theme: Theme) => (this.theme = theme)\n\n /**\n * Recalculate budget by using the average amount from top-level categories\n * @param categories Detailed categories\n */\n recalculateBudgets = async (categories: DetailedCategory[]) => {\n try {\n for (const category of categories) {\n const budget = this.budgets.find((budget) => budget.category_guid === category.guid)\n if (budget) {\n await this.api.updateBudget({\n ...budget,\n amount: category.totalAverageAmount,\n })\n } else {\n await this.api.addBudget(category, category.totalAverageAmount)\n }\n }\n const budgetsToDelete = this.budgets.filter(\n (budget) => !categories.find((c) => c.guid === budget.category_guid),\n )\n\n for (const budgetToDelete of budgetsToDelete) {\n await this.api.deleteBudget(budgetToDelete.guid)\n }\n\n await this.loadBudgets()\n\n return { data: this.budgets, isSuccess: true }\n } catch (error) {\n console.error('Error recalculating budgets:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n // Some budgets may have saved before the failure; reload so a retry updates them.\n await this.loadBudgets().catch(() => undefined)\n return { data: [], isSuccess: false }\n }\n }\n\n updateBudget = async (budget: Budget): Promise<boolean> => {\n try {\n const updatedBudget = await this.api.updateBudget(budget)\n\n runInAction(() => {\n const index = this.budgets.findIndex((b) => b.guid === budget.guid)\n if (index >= 0) {\n this.budgets[index] = updatedBudget\n }\n })\n return true\n } catch (error) {\n console.error('Error updating budget:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n return false\n }\n }\n\n getMerchantBudgetByMerchantGuid = (merchantGuid: string): MerchantBudget | undefined => {\n return this.merchantBudgets.find(\n (eachMerchantBudget) => eachMerchantBudget?.merchant_guid === merchantGuid,\n )\n }\n\n setMerchantBudgets = (merchantBudgets: MerchantBudget[]) => {\n this.merchantBudgets = merchantBudgets\n }\n\n addMerchantBudget = async (merchantGuid: string, amount: number) => {\n try {\n const newMerchantBudget = await this.api.addMerchantBudget(merchantGuid, amount)\n\n runInAction(() => {\n this.merchantBudgets = upsertByGuid(this.merchantBudgets, newMerchantBudget)\n })\n return { isSuccess: true }\n } catch (error) {\n console.error('Error adding merchant budget:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n return { isSuccess: false }\n }\n }\n\n updateMerchantBudget = async (merchantBudget: MerchantBudget) => {\n try {\n const { guid: merchantBudgetGuid, amount } = merchantBudget\n const updatedMerchantBudget = await this.api.updateMerchantBudget(merchantBudgetGuid, amount)\n\n runInAction(() => {\n const index = this.merchantBudgets.findIndex((b) => b.guid === merchantBudget.guid)\n if (index >= 0) {\n this.merchantBudgets[index] = updatedMerchantBudget\n }\n })\n return { isSuccess: true }\n } catch (error) {\n console.error('Error updating merchant budget:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n return { isSuccess: false }\n }\n }\n\n deleteMerchantBudget = async (merchantBudgetGuid: string) => {\n try {\n await this.api.deleteMerchantBudget(merchantBudgetGuid)\n\n runInAction(() => {\n this.merchantBudgets = this.merchantBudgets.filter((b) => b.guid !== merchantBudgetGuid)\n })\n return { isSuccess: true }\n } catch (error) {\n console.error('Error deleting merchant budget:', error)\n this.globalStore.captureException(error, { store: 'BudgetsStore' })\n return { isSuccess: false }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BudgetsStore.es.js","names":[],"sources":["../../../src/core/stores/BudgetsStore.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from 'mobx'\n\nimport { endOfMonth } from 'date-fns/endOfMonth'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfToday } from 'date-fns/startOfToday'\n\nimport { Theme } from '@mui/system'\n\nimport { BudgetsApi } from '../api'\nimport { upsertByGuid } from '../utils/upsertByGuid'\nimport { CategoryGuids } from '../constants'\nimport { augmentBudgets, findBudget, findSubBudget } from '../utils/BudgetUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Budget,\n Category,\n DateRange,\n DetailedBudget,\n DetailedCategory,\n MerchantBudget,\n} from '../types'\n\nexport class BudgetsStore {\n api: BudgetsApi = new BudgetsApi('/', '')\n\n globalStore: GlobalStore\n\n alert: { msg: string; severity: 'success' | 'error' } | null = null\n dateRange: DateRange = { start: startOfMonth(startOfToday()), end: endOfMonth(startOfToday()) }\n budgets: Budget[] = []\n selectedBudget: DetailedBudget | undefined\n selectedSubBudget: DetailedBudget | undefined\n isDataLoaded = false\n theme = {} as Theme\n merchantBudgets: MerchantBudget[] = []\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.api = new BudgetsApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable(this)\n }\n\n loadBudgetData = async () => {\n if (!this.globalStore.recurringTransactionsStore.isDataLoaded) {\n await this.globalStore.recurringTransactionsStore.loadRepeatingTransactionData()\n }\n await this.loadBudgets()\n\n /** Reaction to update the selected budget/sub-budget if detailedBudgets is updated **/\n reaction(\n () => this.detailedBudgets,\n (detailedBudgets) => {\n if (detailedBudgets) {\n const budget = this.selectedBudget\n if (budget) {\n this.selectedBudget = findBudget(detailedBudgets, budget.guid)\n }\n\n const subBudget = this.selectedSubBudget\n if (subBudget && subBudget.parent_guid) {\n this.selectedSubBudget = findSubBudget(\n detailedBudgets,\n subBudget.guid,\n subBudget.parent_guid,\n )\n }\n }\n },\n )\n }\n\n get detailedBudgets(): DetailedBudget[] {\n return augmentBudgets(\n this.budgets.filter(\n (budget: Budget) =>\n budget.category_guid !== CategoryGuids.INCOME &&\n budget.category_guid !== CategoryGuids.TRANSFER &&\n budget.category_guid !== CategoryGuids.INVESTMENTS,\n ),\n this.globalStore.categoryStore.detailedCategories,\n this.globalStore.copyStore.copy.budgets,\n this.theme,\n ).sort((a, b) => {\n if (a.percentage > b.percentage) return -1\n if (a.percentage < b.percentage) return 1\n return 0\n })\n }\n\n // TODO: Replace this with income from recurring transactions. Income budgets are deprecated.\n get incomeBudget(): number | undefined {\n const filteredBudgets = augmentBudgets(\n this.budgets.filter((budget: Budget) => !budget.parent_guid),\n this.globalStore.categoryStore.detailedCategories,\n this.globalStore.copyStore.copy.budgets,\n this.theme,\n )\n\n const incomeBudget = filteredBudgets.reduce(\n (acc, curr) => {\n if (curr.category_guid !== CategoryGuids.INCOME) return acc\n if (!acc || curr.percentage > acc.percentage) return curr\n return acc\n },\n undefined as DetailedBudget | undefined,\n )\n\n return incomeBudget?.amount\n }\n\n get unbudgetedCategories(): DetailedCategory[] {\n return this.globalStore.categoryStore.spendCategories.filter(\n (category) => !this.detailedBudgets.find((budget) => budget.category_guid === category.guid),\n )\n }\n\n get totalBudgeted(): number {\n return this.detailedBudgets.reduce((total, budget) => total + budget.amount, 0)\n }\n\n /** Resolves `true` when the budget was created, `false` when the API call failed. */\n addBudget = async (\n category: Category,\n amount: number,\n parentGuid: string | null = null,\n ): Promise<boolean> => {\n try {\n const budget = await this.api.addBudget(category, amount, parentGuid)\n runInAction(() => {\n this.budgets = upsertByGuid(this.budgets, budget)\n })\n return true\n } catch (error) {\n this.captureError('addBudget', error)\n return false\n }\n }\n\n deleteBudget = async (guid: string): Promise<boolean> => {\n const guids = [guid, ...this.budgets.filter((b) => b.parent_guid === guid).map((b) => b.guid)]\n const results = await Promise.allSettled(guids.map((g) => this.api.deleteBudget(g)))\n const deleted = new Set(guids.filter((_, i) => results[i].status === 'fulfilled'))\n runInAction(() => {\n this.budgets = this.budgets.filter((b) => !deleted.has(b.guid))\n })\n let failed = false\n for (const [i, r] of results.entries()) {\n if (r.status === 'rejected') {\n failed = true\n this.captureError('deleteBudget', r.reason, { guid: guids[i] })\n }\n }\n return !failed\n }\n\n loadBudgets = async () => {\n try {\n const budgets = await this.api.getBudgets()\n this.setBudgets(budgets)\n } catch (error) {\n this.captureError('loadBudgets', error)\n throw error\n }\n }\n\n setAlert = (alert: { msg: string; severity: 'success' | 'error' } | null) => (this.alert = alert)\n\n setBudgets = (budgets: Budget[]) => {\n this.budgets = budgets\n this.isDataLoaded = true\n }\n\n setDateRange = (dateRange: DateRange) => (this.dateRange = dateRange)\n\n setSelectedBudget = (budget: DetailedBudget | undefined) => (this.selectedBudget = budget)\n\n setSelectedSubBudget = (budget: DetailedBudget | undefined) => (this.selectedSubBudget = budget)\n\n setTheme = (theme: Theme) => (this.theme = theme)\n\n /**\n * Recalculate budget by using the average amount from top-level categories\n * @param categories Detailed categories\n */\n recalculateBudgets = async (categories: DetailedCategory[]) => {\n try {\n for (const category of categories) {\n const budget = this.budgets.find((budget) => budget.category_guid === category.guid)\n if (budget) {\n await this.api.updateBudget({\n ...budget,\n amount: category.totalAverageAmount,\n })\n } else {\n await this.api.addBudget(category, category.totalAverageAmount)\n }\n }\n const budgetsToDelete = this.budgets.filter(\n (budget) => !categories.find((c) => c.guid === budget.category_guid),\n )\n\n for (const budgetToDelete of budgetsToDelete) {\n await this.api.deleteBudget(budgetToDelete.guid)\n }\n\n await this.loadBudgets()\n\n return { data: this.budgets, isSuccess: true }\n } catch (error) {\n this.captureError('recalculateBudgets', error)\n // Some budgets may have saved before the failure; reload so a retry updates them.\n await this.loadBudgets().catch(() => undefined)\n return { data: [], isSuccess: false }\n }\n }\n\n updateBudget = async (budget: Budget): Promise<boolean> => {\n try {\n const updatedBudget = await this.api.updateBudget(budget)\n\n runInAction(() => {\n const index = this.budgets.findIndex((b) => b.guid === budget.guid)\n if (index >= 0) {\n this.budgets[index] = updatedBudget\n }\n })\n return true\n } catch (error) {\n this.captureError('updateBudget', error)\n return false\n }\n }\n\n getMerchantBudgetByMerchantGuid = (merchantGuid: string): MerchantBudget | undefined => {\n return this.merchantBudgets.find(\n (eachMerchantBudget) => eachMerchantBudget?.merchant_guid === merchantGuid,\n )\n }\n\n setMerchantBudgets = (merchantBudgets: MerchantBudget[]) => {\n this.merchantBudgets = merchantBudgets\n }\n\n addMerchantBudget = async (merchantGuid: string, amount: number) => {\n try {\n const newMerchantBudget = await this.api.addMerchantBudget(merchantGuid, amount)\n\n runInAction(() => {\n this.merchantBudgets = upsertByGuid(this.merchantBudgets, newMerchantBudget)\n })\n return { isSuccess: true }\n } catch (error) {\n this.captureError('addMerchantBudget', error)\n return { isSuccess: false }\n }\n }\n\n updateMerchantBudget = async (merchantBudget: MerchantBudget) => {\n try {\n const { guid: merchantBudgetGuid, amount } = merchantBudget\n const updatedMerchantBudget = await this.api.updateMerchantBudget(merchantBudgetGuid, amount)\n\n runInAction(() => {\n const index = this.merchantBudgets.findIndex((b) => b.guid === merchantBudget.guid)\n if (index >= 0) {\n this.merchantBudgets[index] = updatedMerchantBudget\n }\n })\n return { isSuccess: true }\n } catch (error) {\n this.captureError('updateMerchantBudget', error)\n return { isSuccess: false }\n }\n }\n\n deleteMerchantBudget = async (merchantBudgetGuid: string) => {\n try {\n await this.api.deleteMerchantBudget(merchantBudgetGuid)\n\n runInAction(() => {\n this.merchantBudgets = this.merchantBudgets.filter((b) => b.guid !== merchantBudgetGuid)\n })\n return { isSuccess: true }\n } catch (error) {\n this.captureError('deleteMerchantBudget', error)\n return { isSuccess: false }\n }\n }\n\n private captureError = (action: string, error: unknown, extra?: Record<string, string>) =>\n this.globalStore.captureException(error, { store: 'BudgetsStore', action, ...extra })\n}\n"],"mappings":";;;;;;;;AAwBA,IAAa,IAAb,MAA0B;AAAA,EACxB,MAAkB,IAAI,EAAW,KAAK,EAAE;AAAA,EAExC;AAAA,EAEA,QAA+D;AAAA,EAC/D,YAAuB;AAAA,IAAE,OAAO,EAAa,EAAa,CAAC;AAAA,IAAG,KAAK,EAAW,EAAa,CAAC;AAAA,EAAE;AAAA,EAC9F,UAAoB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,QAAQ,CAAC;AAAA,EACT,kBAAoC,CAAC;AAAA,EAErC,YAAY,GAA0B;AACpC,SAAK,cAAc,GACnB,KAAK,MAAM,IAAI,EAAW,EAAY,UAAU,EAAY,cAAc,EAAY,OAAO,GAE7F,EAAmB,IAAI;AAAA,EACzB;AAAA,EAEA,iBAAiB,YAAY;AAC3B,IAAK,KAAK,YAAY,2BAA2B,gBAC/C,MAAM,KAAK,YAAY,2BAA2B,6BAA6B,GAEjF,MAAM,KAAK,YAAY,GAGvB,EAAA,MACQ,KAAK,iBAAA,CACV,MAAoB;AACnB,UAAI,GAAiB;AACnB,cAAM,IAAS,KAAK;AACpB,QAAI,MACF,KAAK,iBAAiB,EAAW,GAAiB,EAAO,IAAI;AAG/D,cAAM,IAAY,KAAK;AACvB,QAAI,KAAa,EAAU,gBACzB,KAAK,oBAAoB,EACvB,GACA,EAAU,MACV,EAAU,WACZ;AAAA,MAEJ;AAAA,IACF,CACF;AAAA,EACF;AAAA,EAEA,IAAI,kBAAoC;AACtC,WAAO,EACL,KAAK,QAAQ,OAAA,CACV,MACC,EAAO,kBAAkB,EAAc,UACvC,EAAO,kBAAkB,EAAc,YACvC,EAAO,kBAAkB,EAAc,WAC3C,GACA,KAAK,YAAY,cAAc,oBAC/B,KAAK,YAAY,UAAU,KAAK,SAChC,KAAK,KACP,EAAE,KAAA,CAAM,GAAG,MACL,EAAE,aAAa,EAAE,aAAmB,KACpC,EAAE,aAAa,EAAE,aAAmB,IACjC,CACR;AAAA,EACH;AAAA,EAGA,IAAI,eAAmC;AAiBrC,WAhBwB,EACtB,KAAK,QAAQ,OAAA,CAAQ,MAAmB,CAAC,EAAO,WAAW,GAC3D,KAAK,YAAY,cAAc,oBAC/B,KAAK,YAAY,UAAU,KAAK,SAChC,KAAK,KAGc,EAAgB,OAAA,CAClC,GAAK,MACA,EAAK,kBAAkB,EAAc,SAAe,IACpD,CAAC,KAAO,EAAK,aAAa,EAAI,aAAmB,IAC9C,GAET,MAGK,GAAc;AAAA,EACvB;AAAA,EAEA,IAAI,uBAA2C;AAC7C,WAAO,KAAK,YAAY,cAAc,gBAAgB,OAAA,CACnD,MAAa,CAAC,KAAK,gBAAgB,KAAA,CAAM,MAAW,EAAO,kBAAkB,EAAS,IAAI,CAC7F;AAAA,EACF;AAAA,EAEA,IAAI,gBAAwB;AAC1B,WAAO,KAAK,gBAAgB,OAAA,CAAQ,GAAO,MAAW,IAAQ,EAAO,QAAQ,CAAC;AAAA,EAChF;AAAA,EAGA,YAAY,OACV,GACA,GACA,IAA4B,SACP;AACrB,QAAI;AACF,YAAM,IAAS,MAAM,KAAK,IAAI,UAAU,GAAU,GAAQ,CAAU;AACpE,aAAA,EAAA,MAAkB;AAChB,aAAK,UAAU,EAAa,KAAK,SAAS,CAAM;AAAA,MAClD,CAAC,GACM;AAAA,IACT,SAAS,GAAO;AACd,kBAAK,aAAa,aAAa,CAAK,GAC7B;AAAA,IACT;AAAA,EACF;AAAA,EAEA,eAAe,OAAO,MAAmC;AACvD,UAAM,IAAQ,CAAC,GAAM,GAAG,KAAK,QAAQ,OAAA,CAAQ,MAAM,EAAE,gBAAgB,CAAI,EAAE,IAAA,CAAK,MAAM,EAAE,IAAI,CAAC,GACvF,IAAU,MAAM,QAAQ,WAAW,EAAM,IAAA,CAAK,MAAM,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,GAC7E,IAAU,IAAI,IAAI,EAAM,OAAA,CAAQ,GAAG,MAAM,EAAQ,CAAA,EAAG,WAAW,WAAW,CAAC;AACjF,IAAA,EAAA,MAAkB;AAChB,WAAK,UAAU,KAAK,QAAQ,OAAA,CAAQ,MAAM,CAAC,EAAQ,IAAI,EAAE,IAAI,CAAC;AAAA,IAChE,CAAC;AACD,QAAI,IAAS;AACb,eAAW,CAAC,GAAG,CAAA,KAAM,EAAQ,QAAQ,EACnC,CAAI,EAAE,WAAW,eACf,IAAS,IACT,KAAK,aAAa,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAM,CAAA,EAAG,CAAC;AAGlE,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,cAAc,YAAY;AACxB,QAAI;AACF,YAAM,IAAU,MAAM,KAAK,IAAI,WAAW;AAC1C,WAAK,WAAW,CAAO;AAAA,IACzB,SAAS,GAAO;AACd,iBAAK,aAAa,eAAe,CAAK,GAChC;AAAA,IACR;AAAA,EACF;AAAA,EAEA,WAAA,CAAY,MAAkE,KAAK,QAAQ;AAAA,EAE3F,aAAA,CAAc,MAAsB;AAClC,SAAK,UAAU,GACf,KAAK,eAAe;AAAA,EACtB;AAAA,EAEA,eAAA,CAAgB,MAA0B,KAAK,YAAY;AAAA,EAE3D,oBAAA,CAAqB,MAAwC,KAAK,iBAAiB;AAAA,EAEnF,uBAAA,CAAwB,MAAwC,KAAK,oBAAoB;AAAA,EAEzF,WAAA,CAAY,MAAkB,KAAK,QAAQ;AAAA,EAM3C,qBAAqB,OAAO,MAAmC;AAC7D,QAAI;AACF,iBAAW,KAAY,GAAY;AACjC,cAAM,IAAS,KAAK,QAAQ,KAAA,CAAM,MAAW,EAAO,kBAAkB,EAAS,IAAI;AACnF,QAAI,IACF,MAAM,KAAK,IAAI,aAAa;AAAA,UAC1B,GAAG;AAAA,UACH,QAAQ,EAAS;AAAA,QACnB,CAAC,IAED,MAAM,KAAK,IAAI,UAAU,GAAU,EAAS,kBAAkB;AAAA,MAElE;AACA,YAAM,IAAkB,KAAK,QAAQ,OAAA,CAClC,MAAW,CAAC,EAAW,KAAA,CAAM,MAAM,EAAE,SAAS,EAAO,aAAa,CACrE;AAEA,iBAAW,KAAkB,EAC3B,OAAM,KAAK,IAAI,aAAa,EAAe,IAAI;AAGjD,mBAAM,KAAK,YAAY,GAEhB;AAAA,QAAE,MAAM,KAAK;AAAA,QAAS,WAAW;AAAA,MAAK;AAAA,IAC/C,SAAS,GAAO;AACd,kBAAK,aAAa,sBAAsB,CAAK,GAE7C,MAAM,KAAK,YAAY,EAAE,MAAA,MAAA;AAAA,OAAqB,GACvC;AAAA,QAAE,MAAM,CAAC;AAAA,QAAG,WAAW;AAAA,MAAM;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,eAAe,OAAO,MAAqC;AACzD,QAAI;AACF,YAAM,IAAgB,MAAM,KAAK,IAAI,aAAa,CAAM;AAExD,aAAA,EAAA,MAAkB;AAChB,cAAM,IAAQ,KAAK,QAAQ,UAAA,CAAW,MAAM,EAAE,SAAS,EAAO,IAAI;AAClE,QAAI,KAAS,MACX,KAAK,QAAQ,CAAA,IAAS;AAAA,MAE1B,CAAC,GACM;AAAA,IACT,SAAS,GAAO;AACd,kBAAK,aAAa,gBAAgB,CAAK,GAChC;AAAA,IACT;AAAA,EACF;AAAA,EAEA,kCAAA,CAAmC,MAC1B,KAAK,gBAAgB,KAAA,CACzB,MAAuB,GAAoB,kBAAkB,CAChE;AAAA,EAGF,qBAAA,CAAsB,MAAsC;AAC1D,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,oBAAoB,OAAO,GAAsB,MAAmB;AAClE,QAAI;AACF,YAAM,IAAoB,MAAM,KAAK,IAAI,kBAAkB,GAAc,CAAM;AAE/E,aAAA,EAAA,MAAkB;AAChB,aAAK,kBAAkB,EAAa,KAAK,iBAAiB,CAAiB;AAAA,MAC7E,CAAC,GACM,EAAE,WAAW,GAAK;AAAA,IAC3B,SAAS,GAAO;AACd,kBAAK,aAAa,qBAAqB,CAAK,GACrC,EAAE,WAAW,GAAM;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,uBAAuB,OAAO,MAAmC;AAC/D,QAAI;AACF,YAAM,EAAE,MAAM,GAAoB,QAAA,EAAA,IAAW,GACvC,IAAwB,MAAM,KAAK,IAAI,qBAAqB,GAAoB,CAAM;AAE5F,aAAA,EAAA,MAAkB;AAChB,cAAM,IAAQ,KAAK,gBAAgB,UAAA,CAAW,MAAM,EAAE,SAAS,EAAe,IAAI;AAClF,QAAI,KAAS,MACX,KAAK,gBAAgB,CAAA,IAAS;AAAA,MAElC,CAAC,GACM,EAAE,WAAW,GAAK;AAAA,IAC3B,SAAS,GAAO;AACd,kBAAK,aAAa,wBAAwB,CAAK,GACxC,EAAE,WAAW,GAAM;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,uBAAuB,OAAO,MAA+B;AAC3D,QAAI;AACF,mBAAM,KAAK,IAAI,qBAAqB,CAAkB,GAEtD,EAAA,MAAkB;AAChB,aAAK,kBAAkB,KAAK,gBAAgB,OAAA,CAAQ,MAAM,EAAE,SAAS,CAAkB;AAAA,MACzF,CAAC,GACM,EAAE,WAAW,GAAK;AAAA,IAC3B,SAAS,GAAO;AACd,kBAAK,aAAa,wBAAwB,CAAK,GACxC,EAAE,WAAW,GAAM;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,eAAA,CAAwB,GAAgB,GAAgB,MACtD,KAAK,YAAY,iBAAiB,GAAO;AAAA,IAAE,OAAO;AAAA,IAAgB,QAAA;AAAA,IAAQ,GAAG;AAAA,EAAM,CAAC;AACxF"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CategoryGuidToName as T, CategoryGuids as n } from "../constants/Category.es.js";
|
|
2
|
-
import { toUnixUTC as
|
|
2
|
+
import { toUnixUTC as i } from "../utils/DateUtil.es.js";
|
|
3
3
|
import { CategoryApi as d } from "../api/CategoryApi.es.js";
|
|
4
4
|
import { augmentCategories as C, getDetailedCategoryTransactions as f } from "../utils/CategoryUtil.es.js";
|
|
5
5
|
import { endOfMonth as h } from "date-fns/endOfMonth";
|
|
6
6
|
import { startOfMonth as p } from "date-fns/startOfMonth";
|
|
7
7
|
import { subMonths as c } from "date-fns/subMonths";
|
|
8
8
|
import { makeAutoObservable as u, runInAction as g } from "mobx";
|
|
9
|
-
var
|
|
9
|
+
var E = class {
|
|
10
10
|
globalStore;
|
|
11
11
|
uiStore;
|
|
12
12
|
api = new d("/", "");
|
|
@@ -24,7 +24,7 @@ var A = class {
|
|
|
24
24
|
lastDateRangeTotalsArgs = null;
|
|
25
25
|
lastMonthlyTotalsArgs = null;
|
|
26
26
|
constructor(t) {
|
|
27
|
-
this.globalStore = t, this.uiStore = new
|
|
27
|
+
this.globalStore = t, this.uiStore = new m(), this.api = new d(t.endpoint, t.sessionToken, t.onError), u(this, {
|
|
28
28
|
dateRangeTotalsRequestId: !1,
|
|
29
29
|
monthlyTotalsRequestId: !1,
|
|
30
30
|
lastDateRangeTotalsArgs: !1,
|
|
@@ -65,8 +65,8 @@ var A = class {
|
|
|
65
65
|
};
|
|
66
66
|
try {
|
|
67
67
|
await this.api.addCategory(a), await this.loadCategories();
|
|
68
|
-
} catch (
|
|
69
|
-
|
|
68
|
+
} catch (s) {
|
|
69
|
+
this.captureError("addCategory", s);
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
loadCategories = async () => {
|
|
@@ -76,14 +76,14 @@ var A = class {
|
|
|
76
76
|
const t = await this.api.getCategories();
|
|
77
77
|
this.setCategories(t);
|
|
78
78
|
} catch (t) {
|
|
79
|
-
|
|
79
|
+
this.captureError("loadCategories", t), g(() => {
|
|
80
80
|
this.categoriesLoading = !1;
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
loadDateRangeCategoryTotals = async (t, e, a) => {
|
|
86
|
-
const
|
|
86
|
+
const s = ++this.dateRangeTotalsRequestId, o = () => s !== this.dateRangeTotalsRequestId;
|
|
87
87
|
this.lastDateRangeTotalsArgs = {
|
|
88
88
|
accounts: t,
|
|
89
89
|
startDate: e,
|
|
@@ -92,16 +92,16 @@ var A = class {
|
|
|
92
92
|
this.dateRangeTotalsLoading = !0;
|
|
93
93
|
});
|
|
94
94
|
try {
|
|
95
|
-
const r = await this.api.getDateRangeCategoryTotals(
|
|
96
|
-
if (
|
|
95
|
+
const r = await this.api.getDateRangeCategoryTotals(i(e), i(a), t);
|
|
96
|
+
if (o()) return;
|
|
97
97
|
this.setDateRangeCategoryTotals(r);
|
|
98
98
|
} catch (r) {
|
|
99
|
-
if (
|
|
100
|
-
|
|
99
|
+
if (o()) return;
|
|
100
|
+
this.captureError("loadDateRangeCategoryTotals", r), this.setDateRangeCategoryTotals([]);
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
loadMonthlyCategoryTotals = async (t = this.globalStore.globalUiStore.selectedAccounts, e, a) => {
|
|
104
|
-
const
|
|
104
|
+
const s = ++this.monthlyTotalsRequestId, o = () => s !== this.monthlyTotalsRequestId;
|
|
105
105
|
this.lastMonthlyTotalsArgs = {
|
|
106
106
|
accounts: t,
|
|
107
107
|
startDate: e,
|
|
@@ -109,29 +109,29 @@ var A = class {
|
|
|
109
109
|
}, g(() => {
|
|
110
110
|
this.monthlyTotalsLoading = !0;
|
|
111
111
|
});
|
|
112
|
-
const r =
|
|
112
|
+
const r = i(p(e ?? c(/* @__PURE__ */ new Date(), 11))), y = i(h(a ?? /* @__PURE__ */ new Date()));
|
|
113
113
|
try {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
this.setMonthlyCategoryTotals(
|
|
117
|
-
} catch (
|
|
118
|
-
if (
|
|
119
|
-
|
|
114
|
+
const l = await this.api.getMonthlyCategoryTotals(r, y, t);
|
|
115
|
+
if (o()) return;
|
|
116
|
+
this.setMonthlyCategoryTotals(l);
|
|
117
|
+
} catch (l) {
|
|
118
|
+
if (o()) return;
|
|
119
|
+
this.captureError("loadMonthlyCategoryTotals", l), this.setMonthlyCategoryTotals([]);
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
122
|
reloadCategoryTotals = async (t, e, a) => {
|
|
123
|
-
const
|
|
124
|
-
await Promise.all([this.loadMonthlyCategoryTotals(t,
|
|
123
|
+
const s = e ?? c(/* @__PURE__ */ new Date(), 11), o = a ?? h(/* @__PURE__ */ new Date());
|
|
124
|
+
await Promise.all([this.loadMonthlyCategoryTotals(t, s, o), this.loadDateRangeCategoryTotals(t, s, o)]);
|
|
125
125
|
};
|
|
126
126
|
refreshLoadedCategoryTotals = async () => {
|
|
127
127
|
const t = [];
|
|
128
128
|
if (this.lastDateRangeTotalsArgs) {
|
|
129
|
-
const { accounts: e, startDate: a, endDate:
|
|
130
|
-
t.push(this.loadDateRangeCategoryTotals(e, a,
|
|
129
|
+
const { accounts: e, startDate: a, endDate: s } = this.lastDateRangeTotalsArgs;
|
|
130
|
+
t.push(this.loadDateRangeCategoryTotals(e, a, s));
|
|
131
131
|
}
|
|
132
132
|
if (this.lastMonthlyTotalsArgs) {
|
|
133
|
-
const { accounts: e, startDate: a, endDate:
|
|
134
|
-
t.push(this.loadMonthlyCategoryTotals(e, a,
|
|
133
|
+
const { accounts: e, startDate: a, endDate: s } = this.lastMonthlyTotalsArgs;
|
|
134
|
+
t.push(this.loadMonthlyCategoryTotals(e, a, s));
|
|
135
135
|
}
|
|
136
136
|
await Promise.all(t);
|
|
137
137
|
};
|
|
@@ -139,14 +139,14 @@ var A = class {
|
|
|
139
139
|
try {
|
|
140
140
|
await this.api.removeCategory(t), await this.loadCategories();
|
|
141
141
|
} catch (e) {
|
|
142
|
-
|
|
142
|
+
this.captureError("removeCategory", e);
|
|
143
143
|
}
|
|
144
144
|
};
|
|
145
145
|
updateCategory = async (t) => {
|
|
146
146
|
try {
|
|
147
147
|
await this.api.updateCategory(t), await this.loadCategories();
|
|
148
148
|
} catch (e) {
|
|
149
|
-
|
|
149
|
+
this.captureError("updateCategory", e);
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
152
|
getTopLevelCategoryGuid = (t) => {
|
|
@@ -154,14 +154,18 @@ var A = class {
|
|
|
154
154
|
let a = t;
|
|
155
155
|
for (; !e.has(a); ) {
|
|
156
156
|
e.add(a);
|
|
157
|
-
const
|
|
158
|
-
if (!
|
|
159
|
-
a =
|
|
157
|
+
const s = this.categories.find((o) => o.guid === a)?.parent_guid;
|
|
158
|
+
if (!s) return a;
|
|
159
|
+
a = s;
|
|
160
160
|
}
|
|
161
161
|
return a;
|
|
162
162
|
};
|
|
163
163
|
getCategoryName = (t) => this.categories.find((e) => e.guid === t)?.name ?? T[t];
|
|
164
|
-
|
|
164
|
+
captureError = (t, e) => this.globalStore.captureException(e, {
|
|
165
|
+
store: "CategoryStore",
|
|
166
|
+
action: t
|
|
167
|
+
});
|
|
168
|
+
}, m = class {
|
|
165
169
|
expandedGuid = null;
|
|
166
170
|
selectedGuid = null;
|
|
167
171
|
searchValue = "";
|
|
@@ -184,7 +188,7 @@ var A = class {
|
|
|
184
188
|
setOnSelect = (t) => this.savedOnSelect = t;
|
|
185
189
|
};
|
|
186
190
|
export {
|
|
187
|
-
|
|
191
|
+
E as CategoryStore
|
|
188
192
|
};
|
|
189
193
|
|
|
190
194
|
//# sourceMappingURL=CategoryStore.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n // Args each loader was last requested with; refreshLoadedCategoryTotals replays them.\n private lastDateRangeTotalsArgs: {\n accounts: Account[]\n startDate: Date\n endDate: Date\n } | null = null\n private lastMonthlyTotalsArgs: {\n accounts: Account[]\n startDate?: Date\n endDate?: Date\n } | null = null\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable<CategoryStore, 'lastDateRangeTotalsArgs' | 'lastMonthlyTotalsArgs'>(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n lastDateRangeTotalsArgs: false,\n lastMonthlyTotalsArgs: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while adding category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n console.error('Error occurred while getting categories:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n this.lastDateRangeTotalsArgs = { accounts, startDate, endDate }\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting date range category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n this.lastMonthlyTotalsArgs = { accounts, startDate, endDate }\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting monthly category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n // Re-issues each loader's last request; a replay supersedes an in-flight\n // request via the request-id guard, so the replayed response wins.\n refreshLoadedCategoryTotals = async () => {\n const refreshes: Promise<void>[] = []\n\n if (this.lastDateRangeTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastDateRangeTotalsArgs\n refreshes.push(this.loadDateRangeCategoryTotals(accounts, startDate, endDate))\n }\n if (this.lastMonthlyTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastMonthlyTotalsArgs\n refreshes.push(this.loadMonthlyCategoryTotals(accounts, startDate, endDate))\n }\n\n await Promise.all(refreshes)\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while removing category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while updating category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n /**\n * Walks a category guid up to its top-level parent. mxui's `CategoryIcon` only knows the\n * top-level guids and falls back to the Uncategorized icon for anything else, so callers that\n * only have a (possibly sub-) category guid must resolve it before rendering an icon. Returns\n * the guid unchanged when categories are not loaded yet or the guid is unknown.\n */\n getTopLevelCategoryGuid = (guid: string): string => {\n const seen = new Set<string>()\n let current = guid\n while (!seen.has(current)) {\n seen.add(current)\n const parent = this.categories.find((c) => c.guid === current)?.parent_guid\n if (!parent) return current\n current = parent\n }\n return current\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n // Args each loader was last requested with; refreshLoadedCategoryTotals replays them.\n private lastDateRangeTotalsArgs: {\n accounts: Account[]\n startDate: Date\n endDate: Date\n } | null = null\n private lastMonthlyTotalsArgs: {\n accounts: Account[]\n startDate?: Date\n endDate?: Date\n } | null = null\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable<CategoryStore, 'lastDateRangeTotalsArgs' | 'lastMonthlyTotalsArgs'>(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n lastDateRangeTotalsArgs: false,\n lastMonthlyTotalsArgs: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n this.captureError('addCategory', error)\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n this.captureError('loadCategories', error)\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n this.lastDateRangeTotalsArgs = { accounts, startDate, endDate }\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n this.captureError('loadDateRangeCategoryTotals', error)\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n this.lastMonthlyTotalsArgs = { accounts, startDate, endDate }\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n this.captureError('loadMonthlyCategoryTotals', error)\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n // Re-issues each loader's last request; a replay supersedes an in-flight\n // request via the request-id guard, so the replayed response wins.\n refreshLoadedCategoryTotals = async () => {\n const refreshes: Promise<void>[] = []\n\n if (this.lastDateRangeTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastDateRangeTotalsArgs\n refreshes.push(this.loadDateRangeCategoryTotals(accounts, startDate, endDate))\n }\n if (this.lastMonthlyTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastMonthlyTotalsArgs\n refreshes.push(this.loadMonthlyCategoryTotals(accounts, startDate, endDate))\n }\n\n await Promise.all(refreshes)\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n this.captureError('removeCategory', error)\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n this.captureError('updateCategory', error)\n }\n }\n\n /**\n * Walks a category guid up to its top-level parent. mxui's `CategoryIcon` only knows the\n * top-level guids and falls back to the Uncategorized icon for anything else, so callers that\n * only have a (possibly sub-) category guid must resolve it before rendering an icon. Returns\n * the guid unchanged when categories are not loaded yet or the guid is unknown.\n */\n getTopLevelCategoryGuid = (guid: string): string => {\n const seen = new Set<string>()\n let current = guid\n while (!seen.has(current)) {\n seen.add(current)\n const parent = this.categories.find((c) => c.guid === current)?.parent_guid\n if (!parent) return current\n current = parent\n }\n return current\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n\n private captureError = (action: string, error: unknown) =>\n this.globalStore.captureException(error, { store: 'CategoryStore', action })\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
|
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
import { GlobalCopyApi as
|
|
2
|
-
import { makeAutoObservable as a, runInAction as
|
|
3
|
-
var
|
|
1
|
+
import { GlobalCopyApi as r } from "../api/GlobalCopyApi.es.js";
|
|
2
|
+
import { makeAutoObservable as a, runInAction as i } from "mobx";
|
|
3
|
+
var n = class {
|
|
4
4
|
globalStore;
|
|
5
|
-
api = new
|
|
5
|
+
api = new r("/", "");
|
|
6
6
|
copy = {};
|
|
7
7
|
featureNamespace = "experiences";
|
|
8
8
|
isInitialized = !1;
|
|
9
|
-
constructor(
|
|
10
|
-
this.globalStore =
|
|
9
|
+
constructor(t) {
|
|
10
|
+
this.globalStore = t, this.api = new r(t.endpoint, t.sessionToken, t.onError), a(this);
|
|
11
11
|
}
|
|
12
12
|
loadCopy = async () => {
|
|
13
|
-
await this.getCopy(this.featureNamespace),
|
|
13
|
+
await this.getCopy(this.featureNamespace), i(() => {
|
|
14
14
|
this.isInitialized = !0;
|
|
15
15
|
});
|
|
16
16
|
};
|
|
17
|
-
getCopy = async (
|
|
17
|
+
getCopy = async (t, o = "") => {
|
|
18
18
|
try {
|
|
19
|
-
const e = await this.api.getGlobalCopy(
|
|
20
|
-
|
|
21
|
-
this.copy = e[
|
|
19
|
+
const e = await this.api.getGlobalCopy(t, o);
|
|
20
|
+
i(() => {
|
|
21
|
+
this.copy = e[t];
|
|
22
22
|
});
|
|
23
23
|
} catch (e) {
|
|
24
|
-
|
|
24
|
+
this.captureError("getCopy", e);
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
+
captureError = (t, o) => this.globalStore.captureException(o, {
|
|
28
|
+
store: "GlobalCopyStore",
|
|
29
|
+
action: t
|
|
30
|
+
});
|
|
27
31
|
};
|
|
28
32
|
export {
|
|
29
|
-
|
|
33
|
+
n as GlobalCopyStore
|
|
30
34
|
};
|
|
31
35
|
|
|
32
36
|
//# sourceMappingURL=GlobalCopyStore.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlobalCopyStore.es.js","names":[],"sources":["../../../src/core/stores/GlobalCopyStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\n\nimport { GlobalCopyApi } from '../api'\nimport { GlobalCopy } from '../types'\n\nimport { GlobalStore } from './GlobalStore'\n\nexport class GlobalCopyStore {\n globalStore: GlobalStore\n api: GlobalCopyApi = new GlobalCopyApi('/', '')\n\n copy: GlobalCopy = {} as GlobalCopy\n featureNamespace = 'experiences'\n\n isInitialized = false\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.api = new GlobalCopyApi(\n globalStore.endpoint,\n globalStore.sessionToken,\n globalStore.onError,\n )\n\n makeAutoObservable(this)\n }\n\n loadCopy = async () => {\n await this.getCopy(this.featureNamespace)\n\n runInAction(() => {\n this.isInitialized = true\n })\n }\n\n getCopy = async (namespace: string, translationKey: string = '') => {\n try {\n const newCopy = await this.api.getGlobalCopy(namespace, translationKey)\n\n runInAction(() => {\n this.copy = newCopy[namespace]\n })\n } catch (error) {\n
|
|
1
|
+
{"version":3,"file":"GlobalCopyStore.es.js","names":[],"sources":["../../../src/core/stores/GlobalCopyStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\n\nimport { GlobalCopyApi } from '../api'\nimport { GlobalCopy } from '../types'\n\nimport { GlobalStore } from './GlobalStore'\n\nexport class GlobalCopyStore {\n globalStore: GlobalStore\n api: GlobalCopyApi = new GlobalCopyApi('/', '')\n\n copy: GlobalCopy = {} as GlobalCopy\n featureNamespace = 'experiences'\n\n isInitialized = false\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.api = new GlobalCopyApi(\n globalStore.endpoint,\n globalStore.sessionToken,\n globalStore.onError,\n )\n\n makeAutoObservable(this)\n }\n\n loadCopy = async () => {\n await this.getCopy(this.featureNamespace)\n\n runInAction(() => {\n this.isInitialized = true\n })\n }\n\n getCopy = async (namespace: string, translationKey: string = '') => {\n try {\n const newCopy = await this.api.getGlobalCopy(namespace, translationKey)\n\n runInAction(() => {\n this.copy = newCopy[namespace]\n })\n } catch (error) {\n this.captureError('getCopy', error)\n }\n }\n\n private captureError = (action: string, error: unknown) =>\n this.globalStore.captureException(error, { store: 'GlobalCopyStore', action })\n}\n"],"mappings":";;AAOA,IAAa,IAAb,MAA6B;AAAA,EAC3B;AAAA,EACA,MAAqB,IAAI,EAAc,KAAK,EAAE;AAAA,EAE9C,OAAmB,CAAC;AAAA,EACpB,mBAAmB;AAAA,EAEnB,gBAAgB;AAAA,EAEhB,YAAY,GAA0B;AACpC,SAAK,cAAc,GACnB,KAAK,MAAM,IAAI,EACb,EAAY,UACZ,EAAY,cACZ,EAAY,OACd,GAEA,EAAmB,IAAI;AAAA,EACzB;AAAA,EAEA,WAAW,YAAY;AACrB,UAAM,KAAK,QAAQ,KAAK,gBAAgB,GAExC,EAAA,MAAkB;AAChB,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,OAAO,GAAmB,IAAyB,OAAO;AAClE,QAAI;AACF,YAAM,IAAU,MAAM,KAAK,IAAI,cAAc,GAAW,CAAc;AAEtE,MAAA,EAAA,MAAkB;AAChB,aAAK,OAAO,EAAQ,CAAA;AAAA,MACtB,CAAC;AAAA,IACH,SAAS,GAAO;AACd,WAAK,aAAa,WAAW,CAAK;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,eAAA,CAAwB,GAAgB,MACtC,KAAK,YAAY,iBAAiB,GAAO;AAAA,IAAE,OAAO;AAAA,IAAmB,QAAA;AAAA,EAAO,CAAC;AACjF"}
|
|
@@ -42,7 +42,7 @@ var m = class {
|
|
|
42
42
|
const s = await this.api.getHoldings();
|
|
43
43
|
this.setHoldings(s);
|
|
44
44
|
} catch (s) {
|
|
45
|
-
|
|
45
|
+
this.captureError("loadHoldings", s);
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
setAnalysisType = (s) => {
|
|
@@ -59,9 +59,13 @@ var m = class {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
} catch (t) {
|
|
62
|
-
|
|
62
|
+
this.captureError("updateHoldings", t);
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
|
+
captureError = (s, t) => this.globalStore.captureException(t, {
|
|
66
|
+
store: "HoldingStore",
|
|
67
|
+
action: s
|
|
68
|
+
});
|
|
65
69
|
};
|
|
66
70
|
export {
|
|
67
71
|
m as HoldingStore
|