@zeniai/client-epic-state 5.0.80 → 5.0.81-beta0ND
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/lib/commonStateTypes/cacheOverrideFetchReducer.d.ts +17 -0
- package/lib/commonStateTypes/cacheOverrideFetchReducer.js +20 -0
- package/lib/commonStateTypes/filterPrimitives.d.ts +23 -0
- package/lib/commonStateTypes/filterPrimitives.js +21 -0
- package/lib/commonStateTypes/pusherActions.d.ts +11 -0
- package/lib/commonStateTypes/pusherActions.js +6 -0
- package/lib/coreEpics.js +1 -1
- package/lib/entity/account/accountSelector.d.ts +27 -1
- package/lib/entity/account/accountSelector.js +47 -1
- package/lib/entity/account/accountState.d.ts +6 -1
- package/lib/entity/account/accountState.js +11 -4
- package/lib/entity/account/subAccountSelector.d.ts +1 -1
- package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
- package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
- package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.d.ts +1 -1
- package/lib/entity/chargeCard/chargeCardReducer.d.ts +19 -2
- package/lib/entity/chargeCard/chargeCardReducer.js +35 -2
- package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.d.ts +14 -1
- package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.js +36 -2
- package/lib/entity/class/classSelector.d.ts +35 -0
- package/lib/entity/class/classSelector.js +50 -0
- package/lib/entity/paymentAccount/paymentAccountSelector.d.ts +1 -0
- package/lib/entity/paymentAccount/paymentAccountSelector.js +4 -0
- package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +1 -1
- package/lib/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
- package/lib/entity/tenant/SessionManager.js +6 -3
- package/lib/entity/tenant/clearAllEpic.d.ts +2 -2
- package/lib/entity/tenant/clearAllEpic.js +2 -2
- package/lib/entity/tenant/epic/deleteConnectionEpic.d.ts +1 -1
- package/lib/entity/tenant/epic/initEmailConnectOAuthEpic.d.ts +21 -0
- package/lib/entity/tenant/epic/initEmailConnectOAuthEpic.js +54 -0
- package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.d.ts +1 -1
- package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
- package/lib/entity/tenant/epic/saveConnectorCredentialsEpic.d.ts +3 -3
- package/lib/entity/tenant/epic/saveOAuthConnectionEpic.d.ts +1 -1
- package/lib/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
- package/lib/entity/tenant/tenantPayload.d.ts +1 -0
- package/lib/entity/tenant/tenantReducer.d.ts +38 -25
- package/lib/entity/tenant/tenantReducer.js +71 -6
- package/lib/entity/tenant/tenantState.d.ts +1 -0
- package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
- package/lib/epic.d.ts +13 -11
- package/lib/epic.js +15 -12
- package/lib/esm/commonStateTypes/cacheOverrideFetchReducer.js +16 -0
- package/lib/esm/commonStateTypes/filterPrimitives.js +20 -0
- package/lib/esm/commonStateTypes/pusherActions.js +3 -0
- package/lib/esm/coreEpics.js +1 -1
- package/lib/esm/entity/account/accountSelector.js +46 -1
- package/lib/esm/entity/account/accountState.js +7 -1
- package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
- package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
- package/lib/esm/entity/chargeCard/chargeCardReducer.js +34 -1
- package/lib/esm/entity/chargeCardTransaction/chargeCardTransactionReducer.js +35 -1
- package/lib/esm/entity/class/classSelector.js +48 -0
- package/lib/esm/entity/paymentAccount/paymentAccountSelector.js +3 -0
- package/lib/esm/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
- package/lib/esm/entity/tenant/SessionManager.js +4 -1
- package/lib/esm/entity/tenant/clearAllEpic.js +2 -2
- package/lib/esm/entity/tenant/epic/initEmailConnectOAuthEpic.js +50 -0
- package/lib/esm/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
- package/lib/esm/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
- package/lib/esm/entity/tenant/tenantReducer.js +69 -4
- package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
- package/lib/esm/epic.js +15 -12
- package/lib/esm/index.js +38 -25
- package/lib/esm/reducer.js +2 -2
- package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
- package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
- package/lib/esm/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
- package/lib/esm/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
- package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
- package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -1
- package/lib/esm/view/companyView/helpers/cockpitHelpers.js +1 -1
- package/lib/esm/view/companyView/selector/companyManagementViewSelector.js +1 -1
- package/lib/esm/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
- package/lib/esm/view/creditAgentView/creditAgentViewReducer.js +1 -16
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +11 -1
- package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +21 -1
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +147 -13
- package/lib/esm/view/expenseAutomationView/transactionFilterHelpers.js +258 -0
- package/lib/esm/view/expenseAutomationView/types/completedSubTab.js +17 -0
- package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -2
- package/lib/esm/view/financeStatement/financeStatementSelector.js +1 -1
- package/lib/esm/view/onboardingView/customerView/onboardingCustomerViewReducer.js +61 -50
- package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +87 -0
- package/lib/esm/view/recommendation/recommendationHelper.js +29 -0
- package/lib/esm/view/recommendation/recommendationReducer.js +26 -1
- package/lib/esm/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
- package/lib/esm/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
- package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
- package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
- package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
- package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +26 -9
- package/lib/esm/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
- package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
- package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
- package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
- package/lib/esm/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.js +49 -0
- package/lib/esm/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.js +58 -0
- package/lib/esm/view/spendManagement/commonSetup/kycKybAutofillActions.js +12 -0
- package/lib/esm/view/spendManagement/commonSetup/kycKybParseMapper.js +205 -0
- package/lib/esm/view/spendManagement/commonSetup/setupViewReducer.js +71 -52
- package/lib/esm/view/spendManagement/commonSetup/setupViewSelector.js +2 -0
- package/lib/esm/view/spendManagement/commonSetup/types/kycKybAutofill.js +28 -0
- package/lib/esm/view/spendManagement/spendManagementFilterHelpers.js +4 -2
- package/lib/esm/view/subscriptionView/subscriptionViewSelector.js +1 -1
- package/lib/esm/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +1 -1
- package/lib/esm/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +1 -1
- package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +1 -1
- package/lib/esm/view/taskManager/taskListView/taskListReducer.js +1 -1
- package/lib/esm/view/transactionDetail/transactionDetailReducer.js +121 -6
- package/lib/esm/view/transactionDetail/transactionDetailSelector.js +29 -1
- package/lib/index.d.ts +39 -28
- package/lib/index.js +97 -60
- package/lib/reducer.d.ts +2 -2
- package/lib/reducer.js +2 -2
- package/lib/view/aiAccountantView/aiAccountantViewPayload.d.ts +2 -2
- package/lib/view/aiAccountantView/aiAccountantViewReducer.d.ts +1 -1
- package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
- package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
- package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
- package/lib/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
- package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.d.ts +1 -1
- package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
- package/lib/view/companyView/helpers/cockpitHelpers.js +1 -1
- package/lib/view/companyView/selector/companyManagementViewSelector.js +1 -1
- package/lib/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
- package/lib/view/companyView/types/cockpitTypes.d.ts +1 -1
- package/lib/view/creditAgentView/creditAgentViewPayload.d.ts +0 -3
- package/lib/view/creditAgentView/creditAgentViewReducer.d.ts +1 -1
- package/lib/view/creditAgentView/creditAgentViewReducer.js +2 -17
- package/lib/view/creditAgentView/creditAgentViewState.d.ts +0 -5
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.d.ts +2 -2
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +10 -0
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +2 -1
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +23 -2
- package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +21 -0
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +2 -1
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +28 -5
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
- package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +2 -1
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +16 -0
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +7 -1
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +150 -13
- package/lib/view/expenseAutomationView/transactionFilterHelpers.d.ts +66 -0
- package/lib/view/expenseAutomationView/transactionFilterHelpers.js +262 -0
- package/lib/view/expenseAutomationView/types/completedSubTab.d.ts +10 -0
- package/lib/view/expenseAutomationView/types/completedSubTab.js +21 -0
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +1 -2
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -4
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +44 -2
- package/lib/view/financeStatement/financeStatementSelector.js +1 -1
- package/lib/view/onboardingView/customerView/onboardingCustomerViewReducer.d.ts +25 -2
- package/lib/view/onboardingView/customerView/onboardingCustomerViewReducer.js +62 -51
- package/lib/view/onboardingView/customerView/onboardingCustomerViewState.d.ts +10 -0
- package/lib/view/profitAndLossProjectView/profitAndLossProjectViewSelectorTypes.d.ts +1 -1
- package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.d.ts +9 -0
- package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +91 -0
- package/lib/view/recommendation/recommendationHelper.d.ts +9 -0
- package/lib/view/recommendation/recommendationHelper.js +31 -1
- package/lib/view/recommendation/recommendationReducer.d.ts +20 -1
- package/lib/view/recommendation/recommendationReducer.js +27 -2
- package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
- package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.d.ts +3 -1
- package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
- package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.d.ts +3 -1
- package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
- package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
- package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
- package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
- package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.d.ts +18 -3
- package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +27 -10
- package/lib/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
- package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistory.d.ts +1 -1
- package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.d.ts +3 -1
- package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
- package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
- package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.d.ts +3 -1
- package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
- package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.d.ts +8 -0
- package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.js +53 -0
- package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.d.ts +8 -0
- package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.js +62 -0
- package/lib/view/spendManagement/commonSetup/kycKybAutofillActions.d.ts +27 -0
- package/lib/view/spendManagement/commonSetup/kycKybAutofillActions.js +15 -0
- package/lib/view/spendManagement/commonSetup/kycKybParseMapper.d.ts +24 -0
- package/lib/view/spendManagement/commonSetup/kycKybParseMapper.js +213 -0
- package/lib/view/spendManagement/commonSetup/setupViewReducer.d.ts +24 -2
- package/lib/view/spendManagement/commonSetup/setupViewReducer.js +72 -53
- package/lib/view/spendManagement/commonSetup/setupViewSelector.d.ts +4 -0
- package/lib/view/spendManagement/commonSetup/setupViewSelector.js +2 -0
- package/lib/view/spendManagement/commonSetup/setupViewState.d.ts +12 -0
- package/lib/view/spendManagement/commonSetup/types/kycKybAutofill.d.ts +125 -0
- package/lib/view/spendManagement/commonSetup/types/kycKybAutofill.js +32 -0
- package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +1 -1
- package/lib/view/spendManagement/spendManagementFilterHelpers.d.ts +12 -3
- package/lib/view/spendManagement/spendManagementFilterHelpers.js +4 -2
- package/lib/view/subscriptionView/subscriptionViewSelector.js +1 -1
- package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
- package/lib/view/taskManager/taskListView/taskListReducer.js +1 -1
- package/lib/view/transactionDetail/transactionDetailReducer.d.ts +11 -1
- package/lib/view/transactionDetail/transactionDetailReducer.js +122 -7
- package/lib/view/transactionDetail/transactionDetailSelector.d.ts +2 -0
- package/lib/view/transactionDetail/transactionDetailSelector.js +31 -2
- package/lib/view/transactionDetail/transactionDetailTypes.d.ts +1 -0
- package/lib/view/zeniOAuthView/zeniOAuthSelector.d.ts +1 -1
- package/package.json +10 -4
- package/lib/esm/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -13
- package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
- package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.d.ts +0 -11
- package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -17
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
1
2
|
import flatMap from 'lodash/flatMap';
|
|
2
3
|
import { getCOABalances } from '../../commonStateTypes/coaBalance/coaBalance';
|
|
3
4
|
import { sortComparator } from '../../commonStateTypes/coaBalance/sortBalancesByFreeRangeTotal';
|
|
4
|
-
import { getAccountKey, } from './accountState';
|
|
5
|
+
import { getAccountKey, isPaymentAccountType, } from './accountState';
|
|
5
6
|
function getCommonAccountFields(accountState, id) {
|
|
6
7
|
const key = getAccountKey(id.reportId, id.accountId, id.classesViewParentId, id.accountsViewParentId, id.projectsViewParentId);
|
|
7
8
|
const account = accountState.accountsByKey[key];
|
|
@@ -179,3 +180,47 @@ export const getAccountIdsForLabels = (accountState, labels) => {
|
|
|
179
180
|
});
|
|
180
181
|
return allAccountIdsForLabels;
|
|
181
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* Returns all accounts currently in state (single pass over accountsByKey).
|
|
185
|
+
* Use for dropdowns that need account names and types; map to
|
|
186
|
+
* { accountId, accountName, accountType } as needed.
|
|
187
|
+
*/
|
|
188
|
+
export const getAllAccounts = (accountState) => Object.values(accountState.accountsByKey);
|
|
189
|
+
/**
|
|
190
|
+
* Partitions accounts for transaction filter dropdowns: bank/credit_card → paymentAccountNames,
|
|
191
|
+
* all other account types → categoryOptions. Use for payment_account_name and category filter options.
|
|
192
|
+
*
|
|
193
|
+
* Memoised via `createSelector` so the composite `{categoryOptions,
|
|
194
|
+
* paymentAccountNames}` keeps a stable identity across Redux dispatches that
|
|
195
|
+
* don't change account data. Without this, every consumer using
|
|
196
|
+
* `useSelector(state => getTransactionFilterAccountOptions(state.accountState))`
|
|
197
|
+
* would re-render on every dispatch (the inline call returns a fresh outer
|
|
198
|
+
* object each time, defeating reference equality).
|
|
199
|
+
*/
|
|
200
|
+
export const getTransactionFilterAccountOptions = createSelector(
|
|
201
|
+
// Key the memo on the underlying `accountsByKey` record rather than on
|
|
202
|
+
// `getAllAccounts` output: the latter returns a fresh `Object.values(...)`
|
|
203
|
+
// array on every call, which would defeat reselect's reference-equality
|
|
204
|
+
// check and re-run the projection on every dispatch.
|
|
205
|
+
[(accountState) => accountState.accountsByKey], (accountsByKey) => {
|
|
206
|
+
const accounts = Object.values(accountsByKey);
|
|
207
|
+
const paymentAccountNames = [];
|
|
208
|
+
const categoryOptions = [];
|
|
209
|
+
for (const account of accounts) {
|
|
210
|
+
if (account.accountType == null) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const option = {
|
|
214
|
+
accountId: account.accountId,
|
|
215
|
+
accountName: account.accountName,
|
|
216
|
+
accountType: account.accountType,
|
|
217
|
+
};
|
|
218
|
+
if (isPaymentAccountType(account.accountType)) {
|
|
219
|
+
paymentAccountNames.push(option);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
categoryOptions.push(option);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { categoryOptions, paymentAccountNames };
|
|
226
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getNestedAccountIDStr, } from '../../commonStateTypes/accountView/nestedAccountID';
|
|
2
2
|
import { getNestedClassIDStr, } from '../../commonStateTypes/classesView/nestedClassID';
|
|
3
3
|
import { stringToUnion, stringToUnionStrict, } from '../../commonStateTypes/stringToUnion';
|
|
4
|
-
const ALL_ACCOUNT_TYPES = [
|
|
4
|
+
export const ALL_ACCOUNT_TYPES = [
|
|
5
5
|
'bank',
|
|
6
6
|
'credit_card',
|
|
7
7
|
'expenses',
|
|
@@ -25,6 +25,12 @@ const ALL_ACCOUNT_TYPES = [
|
|
|
25
25
|
];
|
|
26
26
|
export const toAccountType = (v) => stringToUnion(v, ALL_ACCOUNT_TYPES);
|
|
27
27
|
export const toAccountTypeStrict = (v) => stringToUnionStrict(v ?? '', ALL_ACCOUNT_TYPES);
|
|
28
|
+
/** Account types used for payment-account filters (e.g. transaction filter "payment account name"). */
|
|
29
|
+
export const PAYMENT_ACCOUNT_TYPES = ['bank', 'credit_card'];
|
|
30
|
+
export function isPaymentAccountType(accountType) {
|
|
31
|
+
return (accountType != null &&
|
|
32
|
+
PAYMENT_ACCOUNT_TYPES.includes(accountType));
|
|
33
|
+
}
|
|
28
34
|
const ALL_ACCOUNT_LABELS = [
|
|
29
35
|
'uncategorized_income',
|
|
30
36
|
'uncategorized_expense',
|
|
@@ -19,9 +19,7 @@ const toAccuracyByModel = (accuracyPayload) => {
|
|
|
19
19
|
export const toAiAccountantEnrollment = (payload) => ({
|
|
20
20
|
status: toAiAccountantEnrollmentStatus(payload.status),
|
|
21
21
|
accuracy: toAccuracyByModel(payload.accuracy),
|
|
22
|
-
latestTraining: payload.latest_training != null
|
|
23
|
-
? date(payload.latest_training)
|
|
24
|
-
: undefined,
|
|
22
|
+
latestTraining: payload.latest_training != null ? date(payload.latest_training) : undefined,
|
|
25
23
|
latestTransactionSync: payload.latest_transaction_sync != null
|
|
26
24
|
? date(payload.latest_transaction_sync)
|
|
27
25
|
: undefined,
|
|
@@ -27,8 +27,7 @@ const aiAccountantCustomer = createSlice({
|
|
|
27
27
|
const existing = draft.jobsByTenantId[action.payload.tenantId] ?? [];
|
|
28
28
|
const jobMap = new Map(existing.map((job) => [job.jobId, job]));
|
|
29
29
|
mappedJobs.forEach((job) => jobMap.set(job.jobId, job));
|
|
30
|
-
draft.jobsByTenantId[action.payload.tenantId] =
|
|
31
|
-
Array.from(jobMap.values());
|
|
30
|
+
draft.jobsByTenantId[action.payload.tenantId] = Array.from(jobMap.values());
|
|
32
31
|
}
|
|
33
32
|
else {
|
|
34
33
|
draft.jobsByTenantId[action.payload.tenantId] = mappedJobs;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import assignWith from 'lodash/assignWith';
|
|
3
|
+
import { toAmount } from '../../commonStateTypes/amount';
|
|
4
|
+
import { toCardStatusCodeType } from './chargeCard';
|
|
3
5
|
import { mapChargeCardPayloadToChargeCard, } from './chargeCardPayload';
|
|
4
6
|
export const initialState = {
|
|
5
7
|
chargeCardByID: {},
|
|
@@ -35,6 +37,37 @@ const chargeCard = createSlice({
|
|
|
35
37
|
const latestChargeCard = mapChargeCardPayloadToChargeCard(action.payload);
|
|
36
38
|
draft.chargeCardByID[latestChargeCard.id] = latestChargeCard;
|
|
37
39
|
},
|
|
40
|
+
updateChargeCardStatusFromPusher(draft, action) {
|
|
41
|
+
const { cardId, cardStatus, isLockedByAdmin } = action.payload;
|
|
42
|
+
const card = draft.chargeCardByID[cardId];
|
|
43
|
+
if (card != null) {
|
|
44
|
+
card.status = {
|
|
45
|
+
code: toCardStatusCodeType(cardStatus.code),
|
|
46
|
+
name: cardStatus.name,
|
|
47
|
+
};
|
|
48
|
+
if (isLockedByAdmin !== undefined) {
|
|
49
|
+
card.isLockedByAdmin = isLockedByAdmin;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
updateChargeCardSpendingFromPusher(draft, action) {
|
|
54
|
+
const { cardId, currencyCode, currencySymbol } = action.payload;
|
|
55
|
+
const card = draft.chargeCardByID[cardId];
|
|
56
|
+
if (card == null) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const amt = (v) => v !== undefined ? toAmount(v, currencyCode, currencySymbol) : undefined;
|
|
60
|
+
card.balance = amt(action.payload.balance) ?? card.balance;
|
|
61
|
+
card.hold = amt(action.payload.hold) ?? card.hold;
|
|
62
|
+
card.dailyPurchaseSpends =
|
|
63
|
+
amt(action.payload.dailyPurchaseSpends) ?? card.dailyPurchaseSpends;
|
|
64
|
+
card.monthlyPurchaseSpends =
|
|
65
|
+
amt(action.payload.monthlyPurchaseSpends) ?? card.monthlyPurchaseSpends;
|
|
66
|
+
card.dailyWithdrawals =
|
|
67
|
+
amt(action.payload.dailyWithdrawals) ?? card.dailyWithdrawals;
|
|
68
|
+
card.monthlyWithdrawals =
|
|
69
|
+
amt(action.payload.monthlyWithdrawals) ?? card.monthlyWithdrawals;
|
|
70
|
+
},
|
|
38
71
|
removeChargeCard(draft, action) {
|
|
39
72
|
delete draft.chargeCardByID[action.payload];
|
|
40
73
|
},
|
|
@@ -43,5 +76,5 @@ const chargeCard = createSlice({
|
|
|
43
76
|
},
|
|
44
77
|
},
|
|
45
78
|
});
|
|
46
|
-
export const { updateChargeCards, updateChargeCard, removeChargeCard, clearAllChargeCards, } = chargeCard.actions;
|
|
79
|
+
export const { updateChargeCards, updateChargeCard, updateChargeCardStatusFromPusher, updateChargeCardSpendingFromPusher, removeChargeCard, clearAllChargeCards, } = chargeCard.actions;
|
|
47
80
|
export default chargeCard.reducer;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
import { addChargeCardTransactionFromPusher, addDeclinedChargeCardTransactionFromPusher, } from '../../commonStateTypes/pusherActions';
|
|
3
|
+
import { toCardTransactionStatusCode, } from './chargeCardTransaction';
|
|
2
4
|
import { toChargeCardTransaction, } from './chargeCardTransactionPayload';
|
|
3
5
|
export const initialState = {
|
|
4
6
|
chargeCardTransactionById: {},
|
|
@@ -46,10 +48,42 @@ const chargeCardTransaction = createSlice({
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
},
|
|
51
|
+
updateChargeCardTransactionStatusFromPusher(draft, action) {
|
|
52
|
+
const { transactionId, transactionStatus } = action.payload;
|
|
53
|
+
const transaction = draft.chargeCardTransactionById[transactionId];
|
|
54
|
+
if (transaction != null) {
|
|
55
|
+
transaction.transactionStatus = {
|
|
56
|
+
code: toCardTransactionStatusCode(transactionStatus.code),
|
|
57
|
+
name: transactionStatus.name,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
updateChargeCardTransactionFromPusher(draft, action) {
|
|
62
|
+
const entity = toChargeCardTransaction(action.payload.transaction);
|
|
63
|
+
draft.chargeCardTransactionById[entity.id] = entity;
|
|
64
|
+
},
|
|
65
|
+
updateChargeCardTransactionReceiptFromPusher(draft, action) {
|
|
66
|
+
const { transactionId, attachmentFilePaths } = action.payload;
|
|
67
|
+
const transaction = draft.chargeCardTransactionById[transactionId];
|
|
68
|
+
if (transaction != null) {
|
|
69
|
+
transaction.attachmentFilePaths = attachmentFilePaths;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
49
72
|
clearAllChargeCardTransactions(draft) {
|
|
50
73
|
draft.chargeCardTransactionById = {};
|
|
51
74
|
},
|
|
52
75
|
},
|
|
76
|
+
extraReducers: (builder) => {
|
|
77
|
+
builder
|
|
78
|
+
.addCase(addChargeCardTransactionFromPusher, (draft, action) => {
|
|
79
|
+
const entity = toChargeCardTransaction(action.payload.transaction);
|
|
80
|
+
draft.chargeCardTransactionById[entity.id] = entity;
|
|
81
|
+
})
|
|
82
|
+
.addCase(addDeclinedChargeCardTransactionFromPusher, (draft, action) => {
|
|
83
|
+
const entity = toChargeCardTransaction(action.payload.transaction);
|
|
84
|
+
draft.chargeCardTransactionById[entity.id] = entity;
|
|
85
|
+
});
|
|
86
|
+
},
|
|
53
87
|
});
|
|
54
|
-
export const { updateChargeCardTransactions, updateChargeCardTransaction, updateChargeCardTransactionAttachments, removeChargeCardTransaction, removeChargeCardTransactionAttachmentByThirdPartyTransactionId, clearAllChargeCardTransactions, } = chargeCardTransaction.actions;
|
|
88
|
+
export const { updateChargeCardTransactions, updateChargeCardTransaction, updateChargeCardTransactionAttachments, updateChargeCardTransactionStatusFromPusher, updateChargeCardTransactionFromPusher, updateChargeCardTransactionReceiptFromPusher, removeChargeCardTransaction, removeChargeCardTransactionAttachmentByThirdPartyTransactionId, clearAllChargeCardTransactions, } = chargeCardTransaction.actions;
|
|
55
89
|
export default chargeCardTransaction.reducer;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
1
2
|
import recordGet from 'lodash/get';
|
|
2
3
|
import { getCOABalances } from '../../commonStateTypes/coaBalance/coaBalance';
|
|
3
4
|
import { getAccount } from '../account/accountSelector';
|
|
@@ -50,3 +51,50 @@ export function getClassReport(classState, accountState, id, filter) {
|
|
|
50
51
|
accounts: accountReport,
|
|
51
52
|
};
|
|
52
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns all classes currently in state (single pass over classesByKey).
|
|
56
|
+
* Use for dropdowns that need class names; map to { classId, className } as needed.
|
|
57
|
+
*/
|
|
58
|
+
export function getAllClasses(classState) {
|
|
59
|
+
return Object.values(classState.classesByKey);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Memoised projection of `getAllClasses` to the `{classId, className}` shape
|
|
63
|
+
* expected by filter dropdowns. Consumers should prefer this over inlining a
|
|
64
|
+
* `getAllClasses(...).map(...)` inside `useSelector`: the inline map allocates
|
|
65
|
+
* a fresh array on every Redux dispatch, breaking `useSelector`'s
|
|
66
|
+
* reference-equality optimisation and triggering re-renders on every state
|
|
67
|
+
* change unrelated to class data.
|
|
68
|
+
*
|
|
69
|
+
* Two cleanups happen here, intentionally inside the selector so every
|
|
70
|
+
* consumer benefits uniformly:
|
|
71
|
+
* 1. **Drop entries with a blank `className`.** Soft-deleted classes can
|
|
72
|
+
* retain their classId in state while the human name is blanked, which
|
|
73
|
+
* would otherwise render as an empty row in the filter dropdown.
|
|
74
|
+
* 2. **Dedupe by `classId`.** `classesByKey` is keyed by
|
|
75
|
+
* `${classId}-${reportId}` (see `getClassKey`), so a class loaded under
|
|
76
|
+
* multiple report contexts appears as multiple entries from
|
|
77
|
+
* `Object.values(classesByKey)`. The first non-blank entry per
|
|
78
|
+
* `classId` wins.
|
|
79
|
+
*/
|
|
80
|
+
export const getClassFilterOptions = createSelector(
|
|
81
|
+
// Key the memo on the underlying `classesByKey` record rather than on
|
|
82
|
+
// `getAllClasses` output: the latter returns a fresh `Object.values(...)`
|
|
83
|
+
// array on every call, which would defeat reselect's reference-equality
|
|
84
|
+
// check and re-run the projection on every dispatch.
|
|
85
|
+
[(classState) => classState.classesByKey], (classesByKey) => {
|
|
86
|
+
const classes = Object.values(classesByKey);
|
|
87
|
+
const seenClassIds = new Set();
|
|
88
|
+
const options = [];
|
|
89
|
+
for (const c of classes) {
|
|
90
|
+
if (c.classId == null || seenClassIds.has(c.classId)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (c.className == null || c.className.trim() === '') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
seenClassIds.add(c.classId);
|
|
97
|
+
options.push({ classId: c.classId, className: c.className });
|
|
98
|
+
}
|
|
99
|
+
return options;
|
|
100
|
+
});
|
|
@@ -19,3 +19,6 @@ export function getPaymentAccountsByBankAccountIds(paymentAccountState, bankAcco
|
|
|
19
19
|
.filter((value) => value != null);
|
|
20
20
|
return paymentAccounts;
|
|
21
21
|
}
|
|
22
|
+
export function getPlaidPaymentAccounts(paymentAccountState) {
|
|
23
|
+
return values(paymentAccountState.paymentAccountByID).filter((account) => account.provider === 'plaid');
|
|
24
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import recordGet from 'lodash/get';
|
|
2
|
-
import { getCOABalances, } from '../../commonStateTypes/coaBalance/coaBalance';
|
|
3
2
|
import { getSumOfBalancesByGroupedByPeriod } from '../../commonStateTypes/coaBalance/classBalances/getSumOfBalancesGroupedByPeriod';
|
|
3
|
+
import { getCOABalances, } from '../../commonStateTypes/coaBalance/coaBalance';
|
|
4
4
|
import { getNestedAccountReportForAccount, } from '../account/subAccountSelector';
|
|
5
5
|
import { getProjectById } from '../project/projectSelector';
|
|
6
6
|
import { getSectionProjectViewKey } from './sectionProjectView';
|
|
@@ -260,7 +260,10 @@ export class SessionManager {
|
|
|
260
260
|
this.callbacks?.onHeartbeat();
|
|
261
261
|
// Let other tabs know we just fired a heartbeat — they can skip theirs
|
|
262
262
|
// this cycle to avoid N tabs all firing heartbeats.
|
|
263
|
-
this.broadcast({
|
|
263
|
+
this.broadcast({
|
|
264
|
+
type: 'heartbeat-fired',
|
|
265
|
+
timestamp: this.lastHeartbeatTime,
|
|
266
|
+
});
|
|
264
267
|
}
|
|
265
268
|
// ─── BroadcastChannel (cross-tab activity sync) ───────────
|
|
266
269
|
setupBroadcastChannel() {
|
|
@@ -3,13 +3,13 @@ import { filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { clearAllAiAccountantCustomers } from '../../entity/aiAccountantCustomer/aiAccountantCustomerReducer';
|
|
4
4
|
import { clearAiCfo } from '../../entity/aiCfo/aiCfoReducer';
|
|
5
5
|
import { clearAllApprovalRules } from '../../entity/approvalRule/approvalRuleReducer';
|
|
6
|
+
import { clearAllCreditAgent } from '../../entity/creditAgent/creditAgentReducer';
|
|
6
7
|
import { clearAllEntityApprovalStatus } from '../../entity/entityApprovalStatus/entityApprovalStatusReducer';
|
|
7
8
|
import { clearAllMonthEndCloseChecks } from '../../entity/monthEndCloseChecks/monthEndCloseChecksReducer';
|
|
8
9
|
import { clearAllNotifications } from '../../entity/notification/notificationReducer';
|
|
9
10
|
import { clearAllSectionsClassesViewV2 } from '../../entity/sectionClassesViewV2/sectionClassesViewReducer';
|
|
10
11
|
import { clearAllSectionsProjectView } from '../../entity/sectionProjectView/sectionProjectViewReducer';
|
|
11
12
|
import { clearAllTaskGroups } from '../../entity/taskGroup/taskGroupReducer';
|
|
12
|
-
import { clearCannedResponses } from '../../view/taskManager/cannedResponsesView/cannedResponsesReducer';
|
|
13
13
|
import { clearAllToastNotifications } from '../../entity/toastNotification/toastNotificationReducer';
|
|
14
14
|
import { clearAccountList } from '../../view/accountList/accountListReducer';
|
|
15
15
|
import { clearAccountMappingView } from '../../view/accountMappingView/accountMappingReducer';
|
|
@@ -40,7 +40,6 @@ import { clearCompanyHealthMetricView } from '../../view/companyHealthMetricView
|
|
|
40
40
|
import { clearCompanyMonthEndReportView } from '../../view/companyMonthEndReportView/companyMonthEndReportViewReducer';
|
|
41
41
|
import { clearCompanyTaskManagerView } from '../../view/companyTaskManagerView/companyTaskManagerViewReducer';
|
|
42
42
|
import { clearCompanyView } from '../../view/companyView/companyViewReducer';
|
|
43
|
-
import { clearAllCreditAgent } from '../../entity/creditAgent/creditAgentReducer';
|
|
44
43
|
import { clearCreditAgentView } from '../../view/creditAgentView/creditAgentViewReducer';
|
|
45
44
|
import { clearDashboard } from '../../view/dashboard/dashboardReducer';
|
|
46
45
|
import { clearDashboardLayout } from '../../view/dashboardLayout/dashboardLayoutReducer';
|
|
@@ -135,6 +134,7 @@ import { clearZeniAccountSetupView } from '../../view/spendManagement/zeniAccoun
|
|
|
135
134
|
import { clearZeniAccountsConfig } from '../../view/spendManagement/zeniAccounts/zeniAccountsConfig/zeniAccountsConfigReducer';
|
|
136
135
|
import { clearSubscriptionView } from '../../view/subscriptionView/subscriptionViewReducer';
|
|
137
136
|
import { clearTagView } from '../../view/tagView/tagViewReducer';
|
|
137
|
+
import { clearCannedResponses } from '../../view/taskManager/cannedResponsesView/cannedResponsesReducer';
|
|
138
138
|
import { clearTaskDetail } from '../../view/taskManager/taskDetailView/taskDetailReducer';
|
|
139
139
|
import { clearTaskGroupTemplatesView } from '../../view/taskManager/taskGroupTemplateView/taskGroupTemplateViewReducer';
|
|
140
140
|
import { clearTaskGroupView } from '../../view/taskManager/taskGroupView/taskGroupViewReducer';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { from } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
4
|
+
import { initEmailConnectOAuth, initEmailConnectOAuthFailure, initEmailConnectOAuthSuccess, } from '../tenantReducer';
|
|
5
|
+
const defaultNavigate = (url) => {
|
|
6
|
+
// Production path: full-page redirect. The user's session continues on the
|
|
7
|
+
// provider's consent screen and lands back on this app via auth's HTML
|
|
8
|
+
// callback page (which itself redirects to redirect_after_url with the
|
|
9
|
+
// result encoded as a query param).
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
11
|
+
window.location.href = url;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
// Backs the new POST /1.0/connectors/email/<provider>/init endpoint on the
|
|
15
|
+
// auth service. The endpoint creates a CSRF state token in the auth NDB
|
|
16
|
+
// store and returns the provider's authorization_url; this epic then drives
|
|
17
|
+
// a full-page redirect so the user lands on Google's / Microsoft's consent
|
|
18
|
+
// screen. After consent the user lands on auth's callback, which writes the
|
|
19
|
+
// resulting tokens through to tenant and redirects the browser back to
|
|
20
|
+
// ``redirectAfterUrl`` with ``?email_connect_result=<json>`` for the
|
|
21
|
+
// integrations page to consume on mount.
|
|
22
|
+
export const initEmailConnectOAuthEpic = (actions$, _state$, zeniAPI,
|
|
23
|
+
// Test seam: callers can override to assert intended URL without actually
|
|
24
|
+
// navigating. Default is full-page redirect via window.location.
|
|
25
|
+
navigate = defaultNavigate) => actions$.pipe(filter(initEmailConnectOAuth.match), switchMap((action) => zeniAPI
|
|
26
|
+
.postAndGetJSON(`${zeniAPI.apiEndPoints.authMicroServiceBaseUrl}/1.0/connectors/email/${action.payload.provider}/init`, { redirect_after_url: action.payload.redirectAfterUrl }, { 'zeni-tenant-id': action.payload.tenantId })
|
|
27
|
+
.pipe(mergeMap((response) => {
|
|
28
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
29
|
+
// Fire-and-forget side effect — once the browser navigates,
|
|
30
|
+
// any further state we'd dispatch here is irrelevant. We still
|
|
31
|
+
// emit the success action so subscribers (tests, analytics)
|
|
32
|
+
// can observe the URL the epic decided to navigate to.
|
|
33
|
+
navigate(response.data.authorization_url);
|
|
34
|
+
return from([
|
|
35
|
+
initEmailConnectOAuthSuccess(action.payload.tenantId, action.payload.provider, response.data.authorization_url),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
const status = response.status ??
|
|
39
|
+
createZeniAPIStatus(`Failed to start ${action.payload.provider} connection. Please try again.`);
|
|
40
|
+
return from([
|
|
41
|
+
initEmailConnectOAuthFailure(action.payload.tenantId, action.payload.provider, status),
|
|
42
|
+
]);
|
|
43
|
+
}), catchError((error) => {
|
|
44
|
+
const message = error instanceof Error
|
|
45
|
+
? error.message
|
|
46
|
+
: `Unexpected error starting ${action.payload.provider} connection.`;
|
|
47
|
+
return from([
|
|
48
|
+
initEmailConnectOAuthFailure(action.payload.tenantId, action.payload.provider, createZeniAPIStatus(message)),
|
|
49
|
+
]);
|
|
50
|
+
}))));
|
|
@@ -18,9 +18,13 @@ export const saveAPIKeyConnectionEpic = (actions$, _, zeniAPI) => actions$.pipe(
|
|
|
18
18
|
}
|
|
19
19
|
const status = response.status ??
|
|
20
20
|
createZeniAPIStatus('Failed to save connection. Please try again.');
|
|
21
|
-
return from([
|
|
21
|
+
return from([
|
|
22
|
+
saveAPIKeyConnectionFailure(action.payload.tenantId, status),
|
|
23
|
+
]);
|
|
22
24
|
}), catchError((error) => {
|
|
23
|
-
const message = error instanceof Error
|
|
25
|
+
const message = error instanceof Error
|
|
26
|
+
? error.message
|
|
27
|
+
: 'Unexpected error saving connection.';
|
|
24
28
|
return from([
|
|
25
29
|
saveAPIKeyConnectionFailure(action.payload.tenantId, createZeniAPIStatus(message)),
|
|
26
30
|
]);
|
|
@@ -20,9 +20,13 @@ export const saveOAuthConnectionEpic = (actions$, _, zeniAPI) => actions$.pipe(f
|
|
|
20
20
|
}
|
|
21
21
|
const status = response.status ??
|
|
22
22
|
createZeniAPIStatus('Failed to save connection. Please try again.');
|
|
23
|
-
return from([
|
|
23
|
+
return from([
|
|
24
|
+
saveOAuthConnectionFailure(action.payload.tenantId, status),
|
|
25
|
+
]);
|
|
24
26
|
}), catchError((error) => {
|
|
25
|
-
const message = error instanceof Error
|
|
27
|
+
const message = error instanceof Error
|
|
28
|
+
? error.message
|
|
29
|
+
: 'Unexpected error saving connection.';
|
|
26
30
|
return from([
|
|
27
31
|
saveOAuthConnectionFailure(action.payload.tenantId, createZeniAPIStatus(message)),
|
|
28
32
|
]);
|
|
@@ -9,7 +9,7 @@ import { toNumberOfMonthForAverage, toTimeSpanIdForAverage, } from '../../view/n
|
|
|
9
9
|
import { date, dateNow } from '../../zeniDayJS';
|
|
10
10
|
import { toSubscriptionSummary } from '../subscription/subscriptionSummary/subscriptionSummaryReducer';
|
|
11
11
|
import { getInvitationStatus, getUserRoles, toMasterTOSInfo, toUserReimbursementInfo, } from '../userRole/userRolePayload';
|
|
12
|
-
const EXTERNAL_INTEGRATION_TYPES = ['revenue', 'payments'];
|
|
12
|
+
const EXTERNAL_INTEGRATION_TYPES = ['revenue', 'payments', 'email'];
|
|
13
13
|
export const toExternalIntegrationType = (v) => stringToUnion(v, EXTERNAL_INTEGRATION_TYPES);
|
|
14
14
|
const EXTERNAL_SUPPORTED_TOOLS = [
|
|
15
15
|
'chargebee',
|
|
@@ -17,6 +17,8 @@ const EXTERNAL_SUPPORTED_TOOLS = [
|
|
|
17
17
|
'stripe',
|
|
18
18
|
'mercury',
|
|
19
19
|
'paypal',
|
|
20
|
+
'gmail',
|
|
21
|
+
'outlook',
|
|
20
22
|
];
|
|
21
23
|
export const toExternalSupportedTool = (v) => stringToUnion(v, EXTERNAL_SUPPORTED_TOOLS);
|
|
22
24
|
export const initialState = {
|
|
@@ -342,6 +344,7 @@ const tenant = createSlice({
|
|
|
342
344
|
draft.externalConnectionsByTenant[action.payload.tenantId] = {
|
|
343
345
|
fetchState: 'In-Progress',
|
|
344
346
|
accounting: [],
|
|
347
|
+
email: [],
|
|
345
348
|
payments: [],
|
|
346
349
|
revenue: [],
|
|
347
350
|
saveConnectionState: 'Not-Started',
|
|
@@ -359,10 +362,11 @@ const tenant = createSlice({
|
|
|
359
362
|
return { payload: { tenantId, externalConnections } };
|
|
360
363
|
},
|
|
361
364
|
reducer(draft, action) {
|
|
362
|
-
const { accounting, payments, revenue } = mapConnectionsPayloadToState(action.payload.externalConnections);
|
|
365
|
+
const { accounting, email, payments, revenue } = mapConnectionsPayloadToState(action.payload.externalConnections);
|
|
363
366
|
draft.externalConnectionsByTenant[action.payload.tenantId] = {
|
|
364
367
|
fetchState: 'Completed',
|
|
365
368
|
accounting,
|
|
369
|
+
email,
|
|
366
370
|
payments,
|
|
367
371
|
revenue,
|
|
368
372
|
saveConnectionState: 'Completed',
|
|
@@ -384,6 +388,7 @@ const tenant = createSlice({
|
|
|
384
388
|
draft.externalConnectionsByTenant[tenantId] = {
|
|
385
389
|
fetchState: 'Error',
|
|
386
390
|
accounting: [],
|
|
391
|
+
email: [],
|
|
387
392
|
payments: [],
|
|
388
393
|
revenue: [],
|
|
389
394
|
saveConnectionState: 'Error',
|
|
@@ -563,7 +568,12 @@ const tenant = createSlice({
|
|
|
563
568
|
saveConnectorCredentialsSuccess: {
|
|
564
569
|
prepare(tenantId, connectionType, connectionName, connectionPayload) {
|
|
565
570
|
return {
|
|
566
|
-
payload: {
|
|
571
|
+
payload: {
|
|
572
|
+
tenantId,
|
|
573
|
+
connectionType,
|
|
574
|
+
connectionName,
|
|
575
|
+
connectionPayload,
|
|
576
|
+
},
|
|
567
577
|
};
|
|
568
578
|
},
|
|
569
579
|
reducer(draft, action) {
|
|
@@ -592,6 +602,56 @@ const tenant = createSlice({
|
|
|
592
602
|
}
|
|
593
603
|
},
|
|
594
604
|
},
|
|
605
|
+
// Email-connect (Gmail / Outlook) OAuth init. Distinct from the other
|
|
606
|
+
// connector saves because the FE never sees the access_token — the
|
|
607
|
+
// user-facing redirect lands on the auth service's callback, auth
|
|
608
|
+
// exchanges code → tokens and posts them to tenant, then redirects the
|
|
609
|
+
// browser back to ``redirectAfterUrl`` with ``?email_connect_result=...``.
|
|
610
|
+
// The success action carries the provider's authorization_url so the
|
|
611
|
+
// epic can drive a full-page redirect, mirroring the legacy HubSpot
|
|
612
|
+
// flow in zeni-web-app-ui/IntegrationsScreen.
|
|
613
|
+
initEmailConnectOAuth: {
|
|
614
|
+
prepare(tenantId, provider, redirectAfterUrl) {
|
|
615
|
+
return { payload: { tenantId, provider, redirectAfterUrl } };
|
|
616
|
+
},
|
|
617
|
+
reducer(draft, action) {
|
|
618
|
+
const existing = draft.externalConnectionsByTenant[action.payload.tenantId];
|
|
619
|
+
if (existing != null) {
|
|
620
|
+
// Reuse the per-slug saveConnectorCredentialsStateBySlug bucket so
|
|
621
|
+
// the IntegrationsSettingsPage shows the same loading affordance
|
|
622
|
+
// it already uses for Stripe/Mercury/PayPal connect flows.
|
|
623
|
+
existing.saveConnectorCredentialsStateBySlug[action.payload.provider] = 'In-Progress';
|
|
624
|
+
delete existing.saveConnectorCredentialsErrorBySlug[action.payload.provider];
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
initEmailConnectOAuthSuccess: {
|
|
629
|
+
prepare(tenantId, provider, authorizationUrl) {
|
|
630
|
+
return { payload: { tenantId, provider, authorizationUrl } };
|
|
631
|
+
},
|
|
632
|
+
reducer(draft, action) {
|
|
633
|
+
// Intentionally leave state as 'In-Progress' — the epic is about to
|
|
634
|
+
// do a full-page redirect, so any UI state we set here would be
|
|
635
|
+
// overwritten by the navigation anyway. Setting Completed would
|
|
636
|
+
// briefly flash a success state before the redirect, which reads
|
|
637
|
+
// wrong because the connection isn't actually saved yet at this
|
|
638
|
+
// point (it lands after the OAuth round-trip).
|
|
639
|
+
void draft;
|
|
640
|
+
void action;
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
initEmailConnectOAuthFailure: {
|
|
644
|
+
prepare(tenantId, provider, status) {
|
|
645
|
+
return { payload: { tenantId, provider, status } };
|
|
646
|
+
},
|
|
647
|
+
reducer(draft, action) {
|
|
648
|
+
const existing = draft.externalConnectionsByTenant[action.payload.tenantId];
|
|
649
|
+
if (existing != null) {
|
|
650
|
+
existing.saveConnectorCredentialsStateBySlug[action.payload.provider] = 'Error';
|
|
651
|
+
existing.saveConnectorCredentialsErrorBySlug[action.payload.provider] = action.payload.status;
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
},
|
|
595
655
|
deleteConnection: {
|
|
596
656
|
prepare(tenantId, connectionId, connectionType) {
|
|
597
657
|
return { payload: { tenantId, connectionId, connectionType } };
|
|
@@ -780,7 +840,7 @@ const tenant = createSlice({
|
|
|
780
840
|
},
|
|
781
841
|
},
|
|
782
842
|
});
|
|
783
|
-
export const { updateTenants, fetchAllTenants, updateTenantsSuccess, updateTenantsFailure, fetchActiveTenant, updateTenantSuccess, updateTenantFailure, updateCurrentTenant, fetchExcludedResources, updateExcludedResourcesSuccess, updateExcludedResourcesFailure, doSignIn, doMagicLinkSignIn, magicLinkSignInSuccess, magicLinkSignInFailure, sendEmailMagicLinkToUser, sendEmailMagicLinkToUserSuccess, sendEmailMagicLinkToUserFailure, updateSignInState, doSignOut, signOutSuccess, sendSessionHeartbeat, sessionHeartbeatSuccess, sessionHeartbeatFailure, updateLoggedInUser, fetchExternalConnections, saveExternalConnection, saveExternalConnectionSuccess, saveExternalConnectionFailure, fetchExternalConnectionsFailure, fetchExternalConnectionsSuccess, saveAPIKeyConnection, saveAPIKeyConnectionSuccess, saveAPIKeyConnectionFailure, saveOAuthConnection, saveOAuthConnectionSuccess, saveOAuthConnectionFailure, saveConnectorCredentials, saveConnectorCredentialsSuccess, saveConnectorCredentialsFailure, deleteConnection, deleteConnectionSuccess, deleteConnectionFailure, clearAll, fetchSubscriptionSummaryForTenant, updateSubscriptionSummaryForTenantSuccess, updateSubscriptionSummaryForTenantFailure, updateOnboardingTenants, removeOnboardingTenant, updateTenantReimbursementInfo, updateReferViewedForLoggedInUser, updateTreasuryVideoViewedForLoggedInUser, resetSignInState, trigger2FA, updateTenantAccountingClassesEnabled, updateTenantMasterTOSInfo, verifyDeviceWithTwoFA, verifyDeviceWithTwoFASuccess, verifyDeviceWithTwoFAFailure, resendVerifyDeviceOTP, resendVerifyDeviceOTPSuccess, resendVerifyDeviceOTPFailure, } = tenant.actions;
|
|
843
|
+
export const { updateTenants, fetchAllTenants, updateTenantsSuccess, updateTenantsFailure, fetchActiveTenant, updateTenantSuccess, updateTenantFailure, updateCurrentTenant, fetchExcludedResources, updateExcludedResourcesSuccess, updateExcludedResourcesFailure, doSignIn, doMagicLinkSignIn, magicLinkSignInSuccess, magicLinkSignInFailure, sendEmailMagicLinkToUser, sendEmailMagicLinkToUserSuccess, sendEmailMagicLinkToUserFailure, updateSignInState, doSignOut, signOutSuccess, sendSessionHeartbeat, sessionHeartbeatSuccess, sessionHeartbeatFailure, updateLoggedInUser, fetchExternalConnections, saveExternalConnection, saveExternalConnectionSuccess, saveExternalConnectionFailure, fetchExternalConnectionsFailure, fetchExternalConnectionsSuccess, saveAPIKeyConnection, saveAPIKeyConnectionSuccess, saveAPIKeyConnectionFailure, saveOAuthConnection, saveOAuthConnectionSuccess, saveOAuthConnectionFailure, saveConnectorCredentials, saveConnectorCredentialsSuccess, saveConnectorCredentialsFailure, initEmailConnectOAuth, initEmailConnectOAuthSuccess, initEmailConnectOAuthFailure, deleteConnection, deleteConnectionSuccess, deleteConnectionFailure, clearAll, fetchSubscriptionSummaryForTenant, updateSubscriptionSummaryForTenantSuccess, updateSubscriptionSummaryForTenantFailure, updateOnboardingTenants, removeOnboardingTenant, updateTenantReimbursementInfo, updateReferViewedForLoggedInUser, updateTreasuryVideoViewedForLoggedInUser, resetSignInState, trigger2FA, updateTenantAccountingClassesEnabled, updateTenantMasterTOSInfo, verifyDeviceWithTwoFA, verifyDeviceWithTwoFASuccess, verifyDeviceWithTwoFAFailure, resendVerifyDeviceOTP, resendVerifyDeviceOTPSuccess, resendVerifyDeviceOTPFailure, } = tenant.actions;
|
|
784
844
|
export default tenant.reducer;
|
|
785
845
|
/**
|
|
786
846
|
* Converts tenants payload to Tenant and User State
|
|
@@ -907,6 +967,11 @@ export function mapConnectionsPayloadToState(connectionsPayload) {
|
|
|
907
967
|
const { connections } = connectionsPayload;
|
|
908
968
|
return {
|
|
909
969
|
accounting: connections.accounting.map((payload) => toConnection(payload)),
|
|
970
|
+
// Tenant materializes gmail/outlook under the `email` bucket via
|
|
971
|
+
// _THIRD_PARTY_CONNECTORS_TO_MATERIALIZE when their slugs are enabled in
|
|
972
|
+
// third_party_enabled_connectors Statsig config. Default to [] so older
|
|
973
|
+
// tenant deploys (or disabled-everywhere envs) don't blow up here.
|
|
974
|
+
email: (connections.email ?? []).map((payload) => toConnection(payload)),
|
|
910
975
|
payments: connections.payments.map((payload) => toConnection(payload)),
|
|
911
976
|
revenue: (connections.revenue ?? []).map((payload) => toConnection(payload)),
|
|
912
977
|
};
|
|
@@ -23,7 +23,9 @@ const toTransactionLineBase = (payload, lineType, currency) => ({
|
|
|
23
23
|
});
|
|
24
24
|
const toLinkedTransactionLine = (payload, currency) => ({
|
|
25
25
|
...toTransactionLineBase(payload, 'linked_transaction_line', currency),
|
|
26
|
-
linkedTransactions: payload.linked_transactions != null
|
|
26
|
+
linkedTransactions: payload.linked_transactions != null
|
|
27
|
+
? payload.linked_transactions.map((transactionIDPayload) => toTransactionID(transactionIDPayload))
|
|
28
|
+
: [],
|
|
27
29
|
});
|
|
28
30
|
/**
|
|
29
31
|
* Transaction with account and class line payload with COT tracking
|