@zeniai/client-epic-state 5.0.2 → 5.0.3-betaND1

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.
Files changed (81) hide show
  1. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.d.ts +44 -0
  2. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +55 -0
  3. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.d.ts +10 -0
  4. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +51 -0
  5. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.d.ts +48 -0
  6. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.js +66 -0
  7. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerState.d.ts +52 -0
  8. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerState.js +37 -0
  9. package/lib/entity/billPay/billTransaction/billTransactionState.d.ts +2 -2
  10. package/lib/entity/chargeCard/chargeCard.d.ts +1 -1
  11. package/lib/entity/depositAccountTransaction/depositAccountTransaction.d.ts +1 -1
  12. package/lib/entity/entityApprovalStatus/entityApprovalStatusState.d.ts +2 -2
  13. package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +1 -1
  14. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  15. package/lib/entity/tenant/clearAllEpic.d.ts +3 -1
  16. package/lib/entity/tenant/clearAllEpic.js +4 -0
  17. package/lib/entity/userRole/userRoleType.d.ts +2 -2
  18. package/lib/epic.d.ts +5 -1
  19. package/lib/epic.js +5 -1
  20. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +49 -0
  21. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +47 -0
  22. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerSelector.js +62 -0
  23. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerState.js +31 -0
  24. package/lib/esm/entity/tenant/clearAllEpic.js +4 -0
  25. package/lib/esm/epic.js +5 -1
  26. package/lib/esm/index.js +7 -0
  27. package/lib/esm/reducer.js +6 -0
  28. package/lib/esm/view/aiAccountantView/aiAccountantViewPayload.js +2 -0
  29. package/lib/esm/view/aiAccountantView/aiAccountantViewReducer.js +149 -0
  30. package/lib/esm/view/aiAccountantView/aiAccountantViewSelector.js +72 -0
  31. package/lib/esm/view/aiAccountantView/aiAccountantViewState.js +11 -0
  32. package/lib/esm/view/aiAccountantView/epics/cancelAiAccountantOnboardingEpic.js +46 -0
  33. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +27 -0
  34. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +47 -0
  35. package/lib/esm/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +52 -0
  36. package/lib/esm/view/taskManager/taskListView/epics/fetchTaskListEpic.js +6 -3
  37. package/lib/esm/view/taskManager/taskListView/taskListPayload.js +5 -1
  38. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +11 -0
  39. package/lib/esm/view/taskManager/taskListView/taskListSelector.js +27 -4
  40. package/lib/index.d.ts +9 -0
  41. package/lib/index.js +30 -3
  42. package/lib/reducer.d.ts +6 -0
  43. package/lib/reducer.js +6 -0
  44. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  45. package/lib/view/aiAccountantView/aiAccountantViewPayload.d.ts +31 -0
  46. package/lib/view/aiAccountantView/aiAccountantViewPayload.js +8 -0
  47. package/lib/view/aiAccountantView/aiAccountantViewReducer.d.ts +29 -0
  48. package/lib/view/aiAccountantView/aiAccountantViewReducer.js +153 -0
  49. package/lib/view/aiAccountantView/aiAccountantViewSelector.d.ts +14 -0
  50. package/lib/view/aiAccountantView/aiAccountantViewSelector.js +78 -0
  51. package/lib/view/aiAccountantView/aiAccountantViewState.d.ts +26 -0
  52. package/lib/view/aiAccountantView/aiAccountantViewState.js +13 -0
  53. package/lib/view/aiAccountantView/epics/cancelAiAccountantOnboardingEpic.d.ts +33 -0
  54. package/lib/view/aiAccountantView/epics/cancelAiAccountantOnboardingEpic.js +50 -0
  55. package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.d.ts +23 -0
  56. package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +31 -0
  57. package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.d.ts +33 -0
  58. package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +51 -0
  59. package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.d.ts +28 -0
  60. package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +56 -0
  61. package/lib/view/companyView/types/cockpitTypes.d.ts +6 -5
  62. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  63. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +1 -1
  64. package/lib/view/onboardingView/cockpitView/types/onboardingCockpitViewTypes.d.ts +1 -1
  65. package/lib/view/reviewCompanyView/reviewCompanyViewState.d.ts +1 -1
  66. package/lib/view/spendManagement/billPay/billList/billListReducer.d.ts +1 -1
  67. package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
  68. package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetail.d.ts +1 -1
  69. package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.d.ts +4 -4
  70. package/lib/view/spendManagement/reimbursement/remiListView/remiListReducer.d.ts +1 -1
  71. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +3 -3
  72. package/lib/view/subscriptionView/types/subscriptionTypes.d.ts +1 -1
  73. package/lib/view/taskManager/taskListView/epics/fetchTaskListEpic.js +6 -3
  74. package/lib/view/taskManager/taskListView/taskList.d.ts +2 -0
  75. package/lib/view/taskManager/taskListView/taskListPayload.d.ts +10 -1
  76. package/lib/view/taskManager/taskListView/taskListPayload.js +7 -0
  77. package/lib/view/taskManager/taskListView/taskListReducer.d.ts +2 -1
  78. package/lib/view/taskManager/taskListView/taskListReducer.js +11 -0
  79. package/lib/view/taskManager/taskListView/taskListSelector.d.ts +4 -1
  80. package/lib/view/taskManager/taskListView/taskListSelector.js +27 -4
  81. package/package.json +1 -1
package/lib/esm/index.js CHANGED
@@ -606,6 +606,13 @@ export { fetchCompanyTaskManagerView, fetchTaskManagerMetrics, getCompanyTaskMan
606
606
  export { fetchInternationalVerificationForm, submitInternationalVerificationForm, updateVerificationFormLocalData, getIntlWireVerificationView, };
607
607
  export { fetchExpressPayInitialDetails, updateExpressPayFormLocalData, submitExpressPay, resetExpressPayLocalData, getExpressPayView, };
608
608
  export { acceptTreasuryTerms, fetchTreasurySetupView, clearTreasurySetupView, fetchTreasuryFunds, updatePortfolioAllocation, fetchPortfolioAllocation, updateFundAllocationLocalData, updateTreasuryVideoViewed, getTreasurySetupViewDetails, getTreasuryFundsMaximumYield, };
609
+ // ── AI Accountant Entity ──
610
+ export { toAiAccountantEnrollmentStatus, toAiAccountantJobStatus, toAiAccountantOperationType, } from './entity/aiAccountantCustomer/aiAccountantCustomerState';
611
+ export { toAiAccountantCustomer, toAiAccountantEnrollment, toAiAccountantJob, } from './entity/aiAccountantCustomer/aiAccountantCustomerPayload';
612
+ export { clearAllAiAccountantCustomers, updateAiAccountantCustomers, updateAiAccountantCustomer, updateAiAccountantJobs, } from './entity/aiAccountantCustomer/aiAccountantCustomerReducer';
613
+ export { getAiAccountantCustomers, getAiAccountantJobsByTenantId, } from './entity/aiAccountantCustomer/aiAccountantCustomerSelector';
614
+ export { cancelAiAccountantOnboarding, clearAiAccountantView, fetchAiAccountantCustomers, fetchAiAccountantJobs, setSelectedTenantIdsForJobTrigger, triggerAiAccountantJob, updateAiAccountantUIState, } from './view/aiAccountantView/aiAccountantViewReducer';
615
+ export { getAiAccountantCockpitView, } from './view/aiAccountantView/aiAccountantViewSelector';
609
616
  export { fetchChatSessionsForUser, createSession, submitQuestion, updateAiCfoViewScrollPosition, updateCurrentInput, updateCotCollapsedState, clearInput, setSession, clearAiCfoView, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, createSessionAndSubmit, stopSubmit, stopSubmitQuestion, fetchChatHistory, acceptMasterTOS, deleteChatSession, updateResponseState, fetchSuggestedQuestions, fetchSuggestedQuestionsSuccess, fetchSuggestedQuestionsFailure, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, } from './view/aiCfoView/aiCfoViewReducer';
610
617
  export { getAiCfoView, getSuggestedQuestionsForPageContext, } from './view/aiCfoView/aiCfoViewSelector';
611
618
  export { setNewSession, setSessions, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearSession, clearAiCfo, toAiCfoVisualization, } from './entity/aiCfo/aiCfoReducer';
@@ -69,6 +69,8 @@ import accountMapping, { initialState as initialAccountMappingState, } from './v
69
69
  import addressView, { initialState as initialAddressViewState, } from './view/addressView/addressViewReducer';
70
70
  import aggregatedReport, { initialState as initialAggregatedReportViewState, } from './view/aiAgentPerformance/aggregatedReportViewReducer';
71
71
  import aiCfoView, { initialAiCfoViewState, } from './view/aiCfoView/aiCfoViewReducer';
72
+ import aiAccountantCustomer, { initialAiAccountantCustomerState, } from './entity/aiAccountantCustomer/aiAccountantCustomerReducer';
73
+ import aiAccountantView, { initialAiAccountantViewState, } from './view/aiAccountantView/aiAccountantViewReducer';
72
74
  import apAgingDetail, { initialState as initialApAgingDetailState, } from './view/apAgingView/apAgingDetail/apAgingDetailReducer';
73
75
  import apAging, { initialState as initialApAgingState, } from './view/apAgingView/apAgingReport/apAgingReducer';
74
76
  import arAgingDetail, { initialState as initialArAgingDetailState, } from './view/arAgingView/arAgingDetail/arAgingDetailReducer';
@@ -223,6 +225,7 @@ const initialEntitiesState = {
223
225
  accountReconState: initialAccountReconState,
224
226
  accountState: initialAccountState,
225
227
  addressState: initialAddressState,
228
+ aiAccountantCustomerState: initialAiAccountantCustomerState,
226
229
  aiCfoState: initialAiCfoState,
227
230
  approvalRuleState: initialApprovalRuleState,
228
231
  bankAccountState: initialBankAccountState,
@@ -291,6 +294,7 @@ const initialViewsState = {
291
294
  accountMappingState: initialAccountMappingState,
292
295
  addressViewState: initialAddressViewState,
293
296
  aggregatedReportViewState: initialAggregatedReportViewState,
297
+ aiAccountantViewState: initialAiAccountantViewState,
294
298
  aiCfoViewState: initialAiCfoViewState,
295
299
  apAgingDetailState: initialApAgingDetailState,
296
300
  apAgingState: initialApAgingState,
@@ -450,6 +454,7 @@ const entityReducers = {
450
454
  accountingSummaryState: accountingSummary,
451
455
  accountState: account,
452
456
  addressState: address,
457
+ aiAccountantCustomerState: aiAccountantCustomer,
453
458
  aiCfoState: aiCfo,
454
459
  approvalRuleState: approvalRule,
455
460
  bankAccountState: bankAccount,
@@ -519,6 +524,7 @@ const viewReducers = {
519
524
  accountMappingState: accountMapping,
520
525
  addressViewState: addressView,
521
526
  aggregatedReportViewState: aggregatedReport,
527
+ aiAccountantViewState: aiAccountantView,
522
528
  aiCfoViewState: aiCfoView,
523
529
  apAgingDetailState: apAgingDetail,
524
530
  apAgingState: apAging,
@@ -0,0 +1,2 @@
1
+ // Re-export entity payload types and mapping functions.
2
+ export { toAiAccountantCustomer, toAiAccountantEnrollment, toAiAccountantJob, } from '../../entity/aiAccountantCustomer/aiAccountantCustomerPayload';
@@ -0,0 +1,149 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ export const initialAiAccountantViewState = {
3
+ customersByTenantId: {},
4
+ customersFetchStatus: { fetchState: 'Not-Started' },
5
+ selectedTenantIdsForJobTrigger: [],
6
+ triggerJobStatus: { fetchState: 'Not-Started' },
7
+ uiState: {
8
+ filterText: '',
9
+ filters: {},
10
+ selectedCustomerTenantId: '',
11
+ sortKey: 'companyName',
12
+ sortOrder: 'ascending',
13
+ },
14
+ };
15
+ const aiAccountantView = createSlice({
16
+ name: 'aiAccountantView',
17
+ initialState: initialAiAccountantViewState,
18
+ reducers: {
19
+ // ── Customers ──
20
+ fetchAiAccountantCustomers(draft) {
21
+ draft.customersFetchStatus = { fetchState: 'In-Progress' };
22
+ },
23
+ updateAiAccountantCustomers(draft, action) {
24
+ draft.customersByTenantId = toCustomersByTenantId(action.payload, draft.customersByTenantId);
25
+ draft.customersFetchStatus = { fetchState: 'Completed' };
26
+ },
27
+ updateAiAccountantCustomersFailure(draft, action) {
28
+ draft.customersFetchStatus = {
29
+ fetchState: 'Error',
30
+ error: action.payload,
31
+ };
32
+ },
33
+ // ── Jobs ──
34
+ fetchAiAccountantJobs: {
35
+ reducer(draft, action) {
36
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
37
+ if (customerView != null) {
38
+ customerView.jobsFetchStatus = { fetchState: 'In-Progress' };
39
+ }
40
+ },
41
+ prepare(tenantId, fetchNextPage) {
42
+ return { payload: { tenantId, fetchNextPage: fetchNextPage ?? false } };
43
+ },
44
+ },
45
+ updateAiAccountantJobs: {
46
+ reducer(draft, action) {
47
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
48
+ if (customerView != null) {
49
+ customerView.jobsFetchStatus = { fetchState: 'Completed' };
50
+ customerView.jobsPageToken = action.payload.nextPageToken;
51
+ }
52
+ },
53
+ prepare(tenantId, nextPageToken) {
54
+ return { payload: { tenantId, nextPageToken: nextPageToken ?? null } };
55
+ },
56
+ },
57
+ updateAiAccountantJobsFailure(draft, action) {
58
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
59
+ if (customerView != null) {
60
+ customerView.jobsFetchStatus = {
61
+ fetchState: 'Error',
62
+ error: action.payload.error,
63
+ };
64
+ }
65
+ },
66
+ setSelectedTenantIdsForJobTrigger(draft, action) {
67
+ draft.selectedTenantIdsForJobTrigger = action.payload;
68
+ },
69
+ // ── Trigger Job ──
70
+ triggerAiAccountantJob: {
71
+ reducer(draft,
72
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
+ _action) {
74
+ draft.triggerJobStatus = { fetchState: 'In-Progress' };
75
+ },
76
+ prepare(operationType, startDate, endDate) {
77
+ return { payload: { operationType, startDate, endDate } };
78
+ },
79
+ },
80
+ triggerAiAccountantJobSuccess(draft) {
81
+ draft.triggerJobStatus = { fetchState: 'Completed' };
82
+ },
83
+ triggerAiAccountantJobFailure(draft, action) {
84
+ draft.triggerJobStatus = { fetchState: 'Error', error: action.payload };
85
+ },
86
+ // ── Cancel Onboarding ──
87
+ cancelAiAccountantOnboarding: {
88
+ reducer(draft, action) {
89
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
90
+ if (customerView != null) {
91
+ customerView.cancelOnboardingStatus = { fetchState: 'In-Progress' };
92
+ }
93
+ },
94
+ prepare(companyId, tenantId) {
95
+ return { payload: { companyId, tenantId } };
96
+ },
97
+ },
98
+ cancelAiAccountantOnboardingSuccess: {
99
+ reducer(draft, action) {
100
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
101
+ if (customerView != null) {
102
+ customerView.cancelOnboardingStatus = { fetchState: 'Completed' };
103
+ }
104
+ },
105
+ prepare(tenantId) {
106
+ return { payload: { tenantId } };
107
+ },
108
+ },
109
+ cancelAiAccountantOnboardingFailure: {
110
+ reducer(draft, action) {
111
+ const customerView = draft.customersByTenantId[action.payload.tenantId];
112
+ if (customerView != null) {
113
+ customerView.cancelOnboardingStatus = {
114
+ fetchState: 'Error',
115
+ error: action.payload.error,
116
+ };
117
+ }
118
+ },
119
+ prepare(tenantId, error) {
120
+ return { payload: { tenantId, error } };
121
+ },
122
+ },
123
+ // ── UI State ──
124
+ updateAiAccountantUIState(draft, action) {
125
+ Object.assign(draft.uiState, action.payload);
126
+ },
127
+ // ── Clear ──
128
+ clearAiAccountantView() {
129
+ return initialAiAccountantViewState;
130
+ },
131
+ },
132
+ });
133
+ export const { cancelAiAccountantOnboarding, cancelAiAccountantOnboardingFailure, cancelAiAccountantOnboardingSuccess, clearAiAccountantView, fetchAiAccountantCustomers, fetchAiAccountantJobs, setSelectedTenantIdsForJobTrigger, triggerAiAccountantJob, triggerAiAccountantJobFailure, triggerAiAccountantJobSuccess, updateAiAccountantCustomers, updateAiAccountantCustomersFailure, updateAiAccountantJobs, updateAiAccountantJobsFailure, updateAiAccountantUIState, } = aiAccountantView.actions;
134
+ export default aiAccountantView.reducer;
135
+ // ── Helpers ──
136
+ const toCustomersByTenantId = (customers, existing) => {
137
+ const result = {};
138
+ for (const payload of customers) {
139
+ const tenantId = payload.tenant_id;
140
+ const existingState = existing[tenantId];
141
+ result[tenantId] = existingState ?? {
142
+ cancelOnboardingStatus: { fetchState: 'Not-Started' },
143
+ jobsFetchStatus: { fetchState: 'Not-Started' },
144
+ jobsPageToken: null,
145
+ tenantId,
146
+ };
147
+ }
148
+ return result;
149
+ };
@@ -0,0 +1,72 @@
1
+ import { createSelector } from '@reduxjs/toolkit';
2
+ import orderBy from 'lodash/orderBy';
3
+ import { getAiAccountantCustomers, } from '../../entity/aiAccountantCustomer/aiAccountantCustomerSelector';
4
+ export const getAiAccountantCockpitView = createSelector((state) => state.aiAccountantViewState, (state) => state.aiAccountantCustomerState, (state) => state.userState, (viewState, customerEntityState, userState) => {
5
+ const { customersByTenantId, customersFetchStatus, selectedTenantIdsForJobTrigger, triggerJobStatus, uiState, } = viewState;
6
+ const allCustomers = getAiAccountantCustomers(customerEntityState, userState);
7
+ let filteredCustomers = allCustomers;
8
+ // Text filter
9
+ const trimmedFilterText = uiState.filterText.trim();
10
+ if (trimmedFilterText.length > 0) {
11
+ const query = trimmedFilterText.toLowerCase();
12
+ filteredCustomers = filteredCustomers.filter((customer) => customer.companyName.toLowerCase().includes(query) ||
13
+ customer.tenantId.toLowerCase().includes(query));
14
+ }
15
+ // Cockpit filters (e.g. enrollmentStatus)
16
+ const enrollmentStatusFilter = uiState.filters.categories?.find((category) => category.field === 'enrollmentStatus');
17
+ if (enrollmentStatusFilter != null &&
18
+ enrollmentStatusFilter.values.length > 0) {
19
+ const selectedStatuses = enrollmentStatusFilter.values.map(String);
20
+ const isEqual = enrollmentStatusFilter.matchingOperator === 'equal';
21
+ filteredCustomers = filteredCustomers.filter((customer) => {
22
+ const status = customer.enrollment?.status ?? 'unknown';
23
+ const matches = selectedStatuses.includes(status);
24
+ return isEqual ? matches : !matches;
25
+ });
26
+ }
27
+ // Sorting
28
+ const sortedCustomers = getSortedCustomers(filteredCustomers, uiState.sortKey, uiState.sortOrder);
29
+ const selectedCustomersForJobTrigger = allCustomers.filter((customer) => selectedTenantIdsForJobTrigger.includes(customer.tenantId));
30
+ return {
31
+ averageAccuracy: calcOverallAverageAccuracy(allCustomers),
32
+ fetchState: customersFetchStatus.fetchState,
33
+ error: customersFetchStatus.error,
34
+ customers: sortedCustomers,
35
+ customerViewStateByTenantId: customersByTenantId,
36
+ selectedCustomersForJobTrigger,
37
+ triggerJobStatus,
38
+ uiState,
39
+ };
40
+ });
41
+ // ── Helpers ──
42
+ const getSortedCustomers = (customers, sortKey, sortOrder) => {
43
+ const direction = sortOrder === 'ascending' ? 'asc' : 'desc';
44
+ return orderBy(customers, (customer) => {
45
+ switch (sortKey) {
46
+ case 'companyName':
47
+ return customer.companyName.toLowerCase();
48
+ case 'status':
49
+ return (customer.enrollment?.status ?? 'unknown').toLowerCase();
50
+ case 'bookcloseDate':
51
+ return customer.bookcloseDate?.valueOf() ?? 0;
52
+ case 'latestTraining':
53
+ return customer.enrollment?.latestTraining?.valueOf() ?? 0;
54
+ case 'latestSync':
55
+ return customer.enrollment?.latestTransactionSync?.valueOf() ?? 0;
56
+ case 'accuracy':
57
+ return customer.averageAccuracy;
58
+ default:
59
+ return customer.companyName.toLowerCase();
60
+ }
61
+ }, direction);
62
+ };
63
+ const calcOverallAverageAccuracy = (customers) => {
64
+ const customerAccuracies = customers
65
+ .map((customer) => customer.averageAccuracy)
66
+ .filter((accuracy) => accuracy > 0);
67
+ if (customerAccuracies.length === 0) {
68
+ return 0;
69
+ }
70
+ return (customerAccuracies.reduce((sum, accuracy) => sum + accuracy, 0) /
71
+ customerAccuracies.length);
72
+ };
@@ -0,0 +1,11 @@
1
+ import { stringToUnion } from '../../commonStateTypes/stringToUnion';
2
+ const ALL_AI_ACCOUNTANT_SORT_KEYS = [
3
+ 'companyName',
4
+ 'status',
5
+ 'bookcloseDate',
6
+ 'latestTraining',
7
+ 'latestSync',
8
+ 'accuracy',
9
+ ];
10
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+ const toAiAccountantSortKeyType = (v) => stringToUnion(v, ALL_AI_ACCOUNTANT_SORT_KEYS);
@@ -0,0 +1,46 @@
1
+ import { from } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
5
+ import { cancelAiAccountantOnboarding, cancelAiAccountantOnboardingFailure, cancelAiAccountantOnboardingSuccess, fetchAiAccountantCustomers, } from '../aiAccountantViewReducer';
6
+ export const cancelAiAccountantOnboardingEpic = (actions$, _, zeniAPI) => actions$.pipe(filter(cancelAiAccountantOnboarding.match), mergeMap((action) => {
7
+ const { tenantId, companyId } = action.payload;
8
+ const base = `${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels`;
9
+ const url = `${base}/ai-accountant/cancel-onboarding`;
10
+ return zeniAPI
11
+ .postAndGetJSON(url, {
12
+ tenant_id: tenantId,
13
+ company_id: companyId,
14
+ })
15
+ .pipe(mergeMap((response) => {
16
+ if (isSuccessResponse(response)) {
17
+ return from([
18
+ cancelAiAccountantOnboardingSuccess(tenantId),
19
+ fetchAiAccountantCustomers(),
20
+ openSnackbar({
21
+ messageSection: 'common',
22
+ messageText: 'success',
23
+ type: 'success',
24
+ }),
25
+ ]);
26
+ }
27
+ else {
28
+ return from([
29
+ cancelAiAccountantOnboardingFailure(tenantId, response.status),
30
+ openSnackbar({
31
+ messageSection: 'common',
32
+ messageText: 'failed',
33
+ type: 'error',
34
+ }),
35
+ ]);
36
+ }
37
+ }), catchError((error) => from([
38
+ cancelAiAccountantOnboardingFailure(tenantId, createZeniAPIStatus('Unexpected error', 'AI Accountant cancel onboarding failed: ' +
39
+ JSON.stringify(error))),
40
+ openSnackbar({
41
+ messageSection: 'common',
42
+ messageText: 'failed',
43
+ type: 'error',
44
+ }),
45
+ ])));
46
+ }));
@@ -0,0 +1,27 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { updateAiAccountantCustomers as updateAiAccountantCustomerEntities } from '../../../entity/aiAccountantCustomer/aiAccountantCustomerReducer';
4
+ import { updateAllUsers } from '../../../entity/user/userReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
6
+ import { fetchAiAccountantCustomers, updateAiAccountantCustomers, updateAiAccountantCustomersFailure, } from '../aiAccountantViewReducer';
7
+ export const fetchAiAccountantCustomersEpic = (actions$, _, zeniAPI) => actions$.pipe(filter(fetchAiAccountantCustomers.match), switchMap(() => {
8
+ const base = `${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels`;
9
+ const url = `${base}/ai-accountant/customers`;
10
+ return zeniAPI
11
+ .getJSON(url)
12
+ .pipe(mergeMap((response) => {
13
+ if (isSuccessResponse(response)) {
14
+ const customers = response.data?.customers ?? [];
15
+ const users = response.data?.users ?? [];
16
+ return from([
17
+ updateAiAccountantCustomerEntities(customers),
18
+ updateAllUsers({ users }),
19
+ updateAiAccountantCustomers(customers),
20
+ ]);
21
+ }
22
+ else {
23
+ return of(updateAiAccountantCustomersFailure(response.status));
24
+ }
25
+ }), catchError((error) => of(updateAiAccountantCustomersFailure(createZeniAPIStatus('Unexpected error', 'AI Accountant customers fetch failed: ' +
26
+ JSON.stringify(error))))));
27
+ }));
@@ -0,0 +1,47 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { updateAiAccountantJobs as updateAiAccountantJobEntities } from '../../../entity/aiAccountantCustomer/aiAccountantCustomerReducer';
4
+ import { updateAllUsers } from '../../../entity/user/userReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
6
+ import { fetchAiAccountantJobs, updateAiAccountantJobs, updateAiAccountantJobsFailure, } from '../aiAccountantViewReducer';
7
+ export const fetchAiAccountantJobsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchAiAccountantJobs.match), switchMap((action) => {
8
+ const { tenantId, fetchNextPage } = action.payload;
9
+ const rootState = state$.value;
10
+ const customerView = rootState.aiAccountantViewState.customersByTenantId[tenantId];
11
+ const pageToken = fetchNextPage && customerView?.jobsPageToken != null
12
+ ? customerView.jobsPageToken
13
+ : null;
14
+ const params = new URLSearchParams({
15
+ page_size: '50',
16
+ tenant_id: tenantId,
17
+ });
18
+ if (pageToken != null) {
19
+ params.set('page_token', pageToken);
20
+ }
21
+ const base = `${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels`;
22
+ const url = `${base}/ai-accountant/jobs?${params.toString()}`;
23
+ return zeniAPI
24
+ .getJSON(url)
25
+ .pipe(mergeMap((response) => {
26
+ if (isSuccessResponse(response)) {
27
+ const jobs = response.data?.jobs ?? [];
28
+ const users = response.data?.users ?? [];
29
+ const nextPageToken = response.data?.next_page_token ?? null;
30
+ return from([
31
+ updateAllUsers({ users }),
32
+ updateAiAccountantJobEntities(tenantId, jobs, fetchNextPage),
33
+ updateAiAccountantJobs(tenantId, nextPageToken),
34
+ ]);
35
+ }
36
+ else {
37
+ return of(updateAiAccountantJobsFailure({
38
+ tenantId,
39
+ error: response.status,
40
+ }));
41
+ }
42
+ }), catchError((error) => of(updateAiAccountantJobsFailure({
43
+ tenantId,
44
+ error: createZeniAPIStatus('Unexpected error', 'AI Accountant jobs fetch failed: ' +
45
+ JSON.stringify(error)),
46
+ }))));
47
+ }));
@@ -0,0 +1,52 @@
1
+ import { from } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
5
+ import { fetchAiAccountantCustomers, triggerAiAccountantJob, triggerAiAccountantJobFailure, triggerAiAccountantJobSuccess, } from '../aiAccountantViewReducer';
6
+ export const triggerAiAccountantJobEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(triggerAiAccountantJob.match), mergeMap((action) => {
7
+ const { operationType, startDate, endDate } = action.payload;
8
+ const rootState = state$.value;
9
+ const selectedTenantIds = rootState.aiAccountantViewState.selectedTenantIdsForJobTrigger;
10
+ const base = `${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels`;
11
+ const url = `${base}/ai-accountant/jobs`;
12
+ const params = operationType === 'offboard'
13
+ ? {}
14
+ : { start_date: startDate, end_date: endDate };
15
+ return zeniAPI
16
+ .postAndGetJSON(url, {
17
+ tenant_ids: selectedTenantIds,
18
+ operation_type: operationType,
19
+ params,
20
+ })
21
+ .pipe(mergeMap((response) => {
22
+ if (isSuccessResponse(response)) {
23
+ return from([
24
+ triggerAiAccountantJobSuccess(),
25
+ fetchAiAccountantCustomers(),
26
+ openSnackbar({
27
+ messageSection: 'common',
28
+ messageText: 'success',
29
+ type: 'success',
30
+ }),
31
+ ]);
32
+ }
33
+ else {
34
+ return from([
35
+ triggerAiAccountantJobFailure(response.status),
36
+ openSnackbar({
37
+ messageSection: 'common',
38
+ messageText: 'failed',
39
+ type: 'error',
40
+ }),
41
+ ]);
42
+ }
43
+ }), catchError((error) => from([
44
+ triggerAiAccountantJobFailure(createZeniAPIStatus('Unexpected error', 'AI Accountant trigger job failed: ' +
45
+ JSON.stringify(error))),
46
+ openSnackbar({
47
+ messageSection: 'common',
48
+ messageText: 'failed',
49
+ type: 'error',
50
+ }),
51
+ ])));
52
+ }));
@@ -3,16 +3,19 @@ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { updateTags } from '../../../../entity/tag/tagReducer';
4
4
  import { updateTasks } from '../../../../entity/task/taskReducer';
5
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
+ import { flattenTasksAndSubtasksForEntity, } from '../taskListPayload';
6
7
  import { fetchTaskList, updateTaskList, updateTaskListFetchStatus, } from '../taskListReducer';
7
8
  export const fetchTaskListEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchTaskList.match), switchMap(() => {
8
9
  return zeniAPI
9
10
  .getJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks?query=${encodeURIComponent(`{"task_type": "all"}`)}`)
10
11
  .pipe(mergeMap((response) => {
11
12
  if (isSuccessResponse(response) && response.data != null) {
12
- return of(updateTasks(response.data.tasks), updateTaskList({
13
- data: response.data.tasks,
13
+ const { tasks } = response.data;
14
+ const tasksForEntity = flattenTasksAndSubtasksForEntity(tasks);
15
+ return of(updateTasks(tasksForEntity), updateTaskList({
16
+ data: tasks,
14
17
  updateType: 'replace',
15
- }), updateTags(response.data.tasks.map((task) => task.tags).flat()));
18
+ }), updateTags(tasksForEntity.map((task) => task.tags).flat()));
16
19
  }
17
20
  else {
18
21
  return of(updateTaskListFetchStatus({
@@ -1 +1,5 @@
1
- export {};
1
+ /** Parent tasks and nested subtasks as flat `TaskPayload`s for `updateTasks`. */
2
+ export const flattenTasksAndSubtasksForEntity = (tasks) => tasks.flatMap((task) => {
3
+ const { subtasks, ...parent } = task;
4
+ return [parent, ...(subtasks ?? [])];
5
+ });
@@ -30,6 +30,7 @@ const initialTaskIdsByDueDate = {
30
30
  upcoming: [],
31
31
  };
32
32
  export const initialState = {
33
+ subtaskIdsByParentTaskId: {},
33
34
  taskIds: [],
34
35
  taskIdsByGroupsIds: {},
35
36
  taskIdsByPriority: initialTaskIdsByPriority,
@@ -89,6 +90,7 @@ const taskList = createSlice({
89
90
  const { data } = action.payload;
90
91
  draft.fetchState = 'Completed';
91
92
  draft.error = undefined;
93
+ draft.subtaskIdsByParentTaskId = buildSubtaskIdsByParentTaskId(data);
92
94
  const { taskIds, taskIdsGroupedByPriority, taskIdsByGroupIds, taskIdsGroupedByStatus, taskIdsByAssignees, taskIdsByDueDate, taskIdsByTags, } = getGroupedTaskIds(data);
93
95
  draft.taskIds = taskIds;
94
96
  draft.taskIdsByGroupsIds = taskIdsByGroupIds;
@@ -488,6 +490,15 @@ const getTagsGroupKey = (tags) => {
488
490
  tagIds.sort();
489
491
  return tagIds.join(',');
490
492
  };
493
+ const buildSubtaskIdsByParentTaskId = (tasks) => {
494
+ const subtaskIdsByParentTaskId = {};
495
+ tasks.forEach((task) => {
496
+ if (task.subtasks != null && task.subtasks.length > 0) {
497
+ subtaskIdsByParentTaskId[task.task_id] = task.subtasks.map((s) => s.task_id);
498
+ }
499
+ });
500
+ return subtaskIdsByParentTaskId;
501
+ };
491
502
  const getGroupedTaskIds = (tasks) => {
492
503
  const taskIds = [];
493
504
  const taskIdsByGroupIds = {};
@@ -43,6 +43,30 @@ const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
43
43
  }, sortOrder);
44
44
  return tasksInOrder;
45
45
  };
46
+ const taskToTaskWithUserDetails = (task, userState, userRoleState, addressState) => ({
47
+ ...task,
48
+ assigneeUsers: task.assignees
49
+ .map((userId) => getUserAndUserRole(userState, userRoleState, addressState, userId))
50
+ .filter((value) => value != null),
51
+ subtasks: [],
52
+ });
53
+ const attachSubtasksForTask = (taskWithAssignees, taskListState, taskState, userState, userRoleState, addressState) => {
54
+ const subtaskIds = taskListState.subtaskIdsByParentTaskId[taskWithAssignees.id] ?? [];
55
+ if (subtaskIds.length === 0) {
56
+ return { ...taskWithAssignees, subtasks: [] };
57
+ }
58
+ const subtasks = subtaskIds
59
+ .map((subtaskId) => {
60
+ const subtask = getTaskById(taskState, subtaskId);
61
+ if (subtask == null) {
62
+ return undefined;
63
+ }
64
+ const withAssignees = taskToTaskWithUserDetails(subtask, userState, userRoleState, addressState);
65
+ return attachSubtasksForTask(withAssignees, taskListState, taskState, userState, userRoleState, addressState);
66
+ })
67
+ .filter((value) => value != null);
68
+ return { ...taskWithAssignees, subtasks };
69
+ };
46
70
  const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRoleState, addressState, taskIds, tagState, }) => {
47
71
  const { sortKey, searchText } = taskListState.uiState;
48
72
  const sortOrder = getSortOrder(taskListState.uiState.sortOrder);
@@ -50,10 +74,8 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
50
74
  .map((taskId) => {
51
75
  const task = getTaskById(taskState, taskId);
52
76
  if (task != null) {
53
- const assigneeUsers = task.assignees
54
- .map((userId) => getUserAndUserRole(userState, userRoleState, addressState, userId))
55
- .filter((value) => value != null);
56
- return { ...task, assigneeUsers };
77
+ const taskWithAssignees = taskToTaskWithUserDetails(task, userState, userRoleState, addressState);
78
+ return attachSubtasksForTask(taskWithAssignees, taskListState, taskState, userState, userRoleState, addressState);
57
79
  }
58
80
  return undefined;
59
81
  })
@@ -334,6 +356,7 @@ export const getAllTasks = createSelector((state) => state.taskListState, (state
334
356
  return {
335
357
  uiState: taskListState.uiState,
336
358
  filters: taskListState.filters,
359
+ subtaskIdsByParentTaskId: taskListState.subtaskIdsByParentTaskId,
337
360
  taskGroupTemplates,
338
361
  fetchState: fetchState,
339
362
  userList: getUserList(userState, userRoleState, userListViewState, 'taskManagerCandidate'),
package/lib/index.d.ts CHANGED
@@ -863,6 +863,15 @@ export { Country };
863
863
  export { InternationalWireVerificationState, VerificationFormField, VerificationFormLocalData, FieldValueType, fetchInternationalVerificationForm, submitInternationalVerificationForm, updateVerificationFormLocalData, getIntlWireVerificationView, InternationalWireVerificationView, };
864
864
  export { fetchExpressPayInitialDetails, updateExpressPayFormLocalData, submitExpressPay, resetExpressPayLocalData, ExpressPayFormLocalData, ExpressPayView, getExpressPayView, };
865
865
  export { acceptTreasuryTerms, fetchTreasurySetupView, clearTreasurySetupView, fetchTreasuryFunds, updatePortfolioAllocation, fetchPortfolioAllocation, updateFundAllocationLocalData, updateTreasuryVideoViewed, FundAllocationOption, FundComposition, FundData, TreasurySetupView, getTreasurySetupViewDetails, getTreasuryFundsMaximumYield, };
866
+ export { AiAccountantCustomer, AiAccountantCustomerState, AiAccountantEnrollment, AiAccountantEnrollmentStatus, AiAccountantJob, AiAccountantJobStatus, AiAccountantOperationType, toAiAccountantEnrollmentStatus, toAiAccountantJobStatus, toAiAccountantOperationType, } from './entity/aiAccountantCustomer/aiAccountantCustomerState';
867
+ export { AiAccountantCustomerPayload, AiAccountantEnrollmentPayload, AiAccountantJobPayload, toAiAccountantCustomer, toAiAccountantEnrollment, toAiAccountantJob, } from './entity/aiAccountantCustomer/aiAccountantCustomerPayload';
868
+ export { AiAccountantCancelOnboardingResponse, AiAccountantCancelOnboardingResponseData, AiAccountantCustomersResponse, AiAccountantCustomersResponseData, AiAccountantJobsResponse, AiAccountantJobsResponseData, AiAccountantTriggerJobResponse, AiAccountantTriggerJobResponseData, } from './view/aiAccountantView/aiAccountantViewPayload';
869
+ export { clearAllAiAccountantCustomers, updateAiAccountantCustomers, updateAiAccountantCustomer, updateAiAccountantJobs, } from './entity/aiAccountantCustomer/aiAccountantCustomerReducer';
870
+ export { AiAccountantCustomerView, AiAccountantEnrollmentView, AiAccountantJobView, getAiAccountantCustomers, getAiAccountantJobsByTenantId, } from './entity/aiAccountantCustomer/aiAccountantCustomerSelector';
871
+ export { AiAccountantCustomerViewState, AiAccountantSortKey, AiAccountantUIState, AiAccountantViewState, } from './view/aiAccountantView/aiAccountantViewState';
872
+ export { AiAccountantFilterCategoryField } from './view/companyView/types/cockpitTypes';
873
+ export { cancelAiAccountantOnboarding, clearAiAccountantView, fetchAiAccountantCustomers, fetchAiAccountantJobs, setSelectedTenantIdsForJobTrigger, triggerAiAccountantJob, updateAiAccountantUIState, } from './view/aiAccountantView/aiAccountantViewReducer';
874
+ export { AiAccountantCockpitView, getAiAccountantCockpitView, } from './view/aiAccountantView/aiAccountantViewSelector';
866
875
  export { fetchChatSessionsForUser, createSession, submitQuestion, updateAiCfoViewScrollPosition, updateCurrentInput, updateCotCollapsedState, clearInput, setSession, clearAiCfoView, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, createSessionAndSubmit, stopSubmit, stopSubmitQuestion, fetchChatHistory, acceptMasterTOS, deleteChatSession, updateResponseState, fetchSuggestedQuestions, fetchSuggestedQuestionsSuccess, fetchSuggestedQuestionsFailure, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, } from './view/aiCfoView/aiCfoViewReducer';
867
876
  export { AiCfoViewState, AiCfoViewUIState, SuggestedQuestionsForPageContext, } from './view/aiCfoView/aiCfoViewState';
868
877
  export { FetchSuggestedQuestionsResponse, SuggestedQuestionsDataPayload, } from './view/aiCfoView/aiCfoViewPayload';