@zeniai/client-epic-state 5.0.38 → 5.0.41-betaRD1
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/entity/chargeCardRepayment/chargeCardRepayment.d.ts +1 -1
- package/lib/entity/creditAgent/creditAgentPayload.d.ts +41 -0
- package/lib/entity/creditAgent/creditAgentPayload.js +40 -0
- package/lib/entity/creditAgent/creditAgentReducer.d.ts +9 -0
- package/lib/entity/creditAgent/creditAgentReducer.js +37 -0
- package/lib/entity/creditAgent/creditAgentSelector.d.ts +3 -0
- package/lib/entity/creditAgent/creditAgentSelector.js +10 -0
- package/lib/entity/creditAgent/creditAgentState.d.ts +39 -0
- package/lib/entity/creditAgent/creditAgentState.js +17 -0
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +3 -0
- package/lib/entity/task/taskPayload.d.ts +1 -0
- package/lib/entity/task/taskPayload.js +5 -0
- package/lib/entity/task/taskState.d.ts +5 -0
- package/lib/entity/task/taskState.js +6 -1
- package/lib/entity/tenant/clearAllEpic.d.ts +5 -3
- package/lib/entity/tenant/clearAllEpic.js +6 -2
- package/lib/entity/tenant/tenantReducer.js +18 -2
- package/lib/epic.d.ts +7 -1
- package/lib/epic.js +7 -1
- package/lib/esm/entity/creditAgent/creditAgentPayload.js +36 -0
- package/lib/esm/entity/creditAgent/creditAgentReducer.js +33 -0
- package/lib/esm/entity/creditAgent/creditAgentSelector.js +6 -0
- package/lib/esm/entity/creditAgent/creditAgentState.js +13 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +3 -0
- package/lib/esm/entity/task/taskPayload.js +6 -1
- package/lib/esm/entity/task/taskState.js +3 -0
- package/lib/esm/entity/tenant/clearAllEpic.js +6 -2
- package/lib/esm/entity/tenant/tenantReducer.js +18 -2
- package/lib/esm/epic.js +7 -1
- package/lib/esm/index.js +15 -9
- package/lib/esm/reducer.js +9 -3
- package/lib/esm/view/common/recurringViewHelper.js +17 -0
- package/lib/esm/view/creditAgentView/buildCreditReportCsv.js +44 -0
- package/lib/esm/view/creditAgentView/creditAgentViewPayload.js +2 -0
- package/lib/esm/view/creditAgentView/creditAgentViewReducer.js +183 -0
- package/lib/esm/view/creditAgentView/creditAgentViewSelector.js +85 -0
- package/lib/esm/view/creditAgentView/creditAgentViewState.js +20 -0
- package/lib/esm/view/creditAgentView/epics/fetchCardProfilesEpic.js +20 -0
- package/lib/esm/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +13 -0
- package/lib/esm/view/creditAgentView/epics/fetchCreditAgentMacroEpic.js +19 -0
- package/lib/esm/view/creditAgentView/epics/saveCreditAgentMacroEpic.js +58 -0
- package/lib/esm/view/creditAgentView/epics/scheduleTenantCreditScoreCronEpic.js +39 -0
- package/lib/esm/view/creditAgentView/epics/updateCardProfileEpic.js +62 -0
- package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +2 -0
- package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +5 -0
- package/lib/esm/view/taskManager/taskDetailView/taskDetail.js +1 -0
- package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +5 -0
- package/lib/index.d.ts +21 -13
- package/lib/index.js +77 -46
- package/lib/reducer.d.ts +9 -3
- package/lib/reducer.js +9 -3
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
- package/lib/view/common/recurringViewHelper.d.ts +3 -2
- package/lib/view/common/recurringViewHelper.js +18 -1
- package/lib/view/creditAgentView/buildCreditReportCsv.d.ts +17 -0
- package/lib/view/creditAgentView/buildCreditReportCsv.js +47 -0
- package/lib/view/creditAgentView/creditAgentViewPayload.d.ts +9 -0
- package/lib/view/creditAgentView/creditAgentViewPayload.js +7 -0
- package/lib/view/creditAgentView/creditAgentViewReducer.d.ts +41 -0
- package/lib/view/creditAgentView/creditAgentViewReducer.js +187 -0
- package/lib/view/creditAgentView/creditAgentViewSelector.d.ts +20 -0
- package/lib/view/creditAgentView/creditAgentViewSelector.js +93 -0
- package/lib/view/creditAgentView/creditAgentViewState.d.ts +34 -0
- package/lib/view/creditAgentView/creditAgentViewState.js +22 -0
- package/lib/view/creditAgentView/epics/fetchCardProfilesEpic.d.ts +15 -0
- package/lib/view/creditAgentView/epics/fetchCardProfilesEpic.js +24 -0
- package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.d.ts +11 -0
- package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +17 -0
- package/lib/view/creditAgentView/epics/fetchCreditAgentMacroEpic.d.ts +15 -0
- package/lib/view/creditAgentView/epics/fetchCreditAgentMacroEpic.js +23 -0
- package/lib/view/creditAgentView/epics/saveCreditAgentMacroEpic.d.ts +29 -0
- package/lib/view/creditAgentView/epics/saveCreditAgentMacroEpic.js +62 -0
- package/lib/view/creditAgentView/epics/scheduleTenantCreditScoreCronEpic.d.ts +30 -0
- package/lib/view/creditAgentView/epics/scheduleTenantCreditScoreCronEpic.js +43 -0
- package/lib/view/creditAgentView/epics/updateCardProfileEpic.d.ts +37 -0
- package/lib/view/creditAgentView/epics/updateCardProfileEpic.js +66 -0
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +2 -0
- package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +5 -0
- package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +3 -1
- package/lib/view/taskManager/taskDetailView/taskDetail.js +1 -0
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +1 -0
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +5 -0
- package/package.json +1 -1
package/lib/esm/reducer.js
CHANGED
|
@@ -3,6 +3,8 @@ import account, { initialState as initialAccountState, } from './entity/account/
|
|
|
3
3
|
import accountGroup, { initialState as initialAccountGroupState, } from './entity/accountGroup/accountGroupReducer';
|
|
4
4
|
import accountRecon, { initialState as initialAccountReconState, } from './entity/accountRecon/accountReconReducer';
|
|
5
5
|
import address, { initialState as initialAddressState, } from './entity/address/addressReducer';
|
|
6
|
+
import aiAccountantCustomer, { initialAiAccountantCustomerState, } from './entity/aiAccountantCustomer/aiAccountantCustomerReducer';
|
|
7
|
+
import creditAgentEntity, { initialCreditAgentEntityState, } from './entity/creditAgent/creditAgentReducer';
|
|
6
8
|
import aiCfo, { initialAiCfoState } from './entity/aiCfo/aiCfoReducer';
|
|
7
9
|
import approvalRule, { initialState as initialApprovalRuleState, } from './entity/approvalRule/approvalRuleReducer';
|
|
8
10
|
import bankAccount, { initialState as initialBankAccountState, } from './entity/bankAccount/bankAccountReducer';
|
|
@@ -14,7 +16,6 @@ import chargeCard, { initialState as initialChargeCardState, } from './entity/ch
|
|
|
14
16
|
import chargeCardRepayment, { initialState as initialChargeCardRepaymentState, } from './entity/chargeCardRepayment/chargeCardRepaymentReducer';
|
|
15
17
|
import chargeCardTransaction, { initialState as initialChargeCardTransactionState, } from './entity/chargeCardTransaction/chargeCardTransactionReducer';
|
|
16
18
|
import classes, { initialState as initialClassState, } from './entity/class/classReducer';
|
|
17
|
-
import project, { initialState as initialProjectState, } from './entity/project/projectReducer';
|
|
18
19
|
import company, { initialState as initialCompanyState, } from './entity/company/companyReducer';
|
|
19
20
|
import companyHealthMetric, { initialState as initialCompanyHealthMetricState, } from './entity/companyHealthMetric/companyHealthMetricReducer';
|
|
20
21
|
import connectedAccount, { initialState as initialConnectedAccountState, } from './entity/connectedAccount/connectedAccountReducer';
|
|
@@ -39,6 +40,7 @@ import onboardingCustomer, { initialState as initialOnboardingCustomerState, } f
|
|
|
39
40
|
import paymentAccount, { initialState as initialPaymentAccountState, } from './entity/paymentAccount/paymentAccountReducer';
|
|
40
41
|
import paymentInstrument, { initialState as initialPaymentInstrumentState, } from './entity/paymentInstrument/paymentInstrumentReducer';
|
|
41
42
|
import accountingSummary, { initialState as initialAccountingSummaryState, } from './entity/portfolio/accountingSummary/accountingSummaryReducer';
|
|
43
|
+
import project, { initialState as initialProjectState, } from './entity/project/projectReducer';
|
|
42
44
|
import reimbursement, { initialState as initialReimbursementState, } from './entity/reimbursement/reimbursementReducer';
|
|
43
45
|
import sectionAccountsView, { initialState as initialSectionAccountsViewState, } from './entity/sectionAccountsView/sectionAccountsViewReducer';
|
|
44
46
|
import sectionClassesViewV2, { initialState as initialSectionClassesViewStateV2, } from './entity/sectionClassesViewV2/sectionClassesViewReducer';
|
|
@@ -67,10 +69,9 @@ import vendorExpense, { initialState as initialVendorExpenseState, } from './ent
|
|
|
67
69
|
import accountList, { initialState as initialAccountListState, } from './view/accountList/accountListReducer';
|
|
68
70
|
import accountMapping, { initialState as initialAccountMappingState, } from './view/accountMappingView/accountMappingReducer';
|
|
69
71
|
import addressView, { initialState as initialAddressViewState, } from './view/addressView/addressViewReducer';
|
|
72
|
+
import aiAccountantView, { initialAiAccountantViewState, } from './view/aiAccountantView/aiAccountantViewReducer';
|
|
70
73
|
import aggregatedReport, { initialState as initialAggregatedReportViewState, } from './view/aiAgentPerformance/aggregatedReportViewReducer';
|
|
71
74
|
import aiCfoView, { initialAiCfoViewState, } from './view/aiCfoView/aiCfoViewReducer';
|
|
72
|
-
import aiAccountantCustomer, { initialAiAccountantCustomerState, } from './entity/aiAccountantCustomer/aiAccountantCustomerReducer';
|
|
73
|
-
import aiAccountantView, { initialAiAccountantViewState, } from './view/aiAccountantView/aiAccountantViewReducer';
|
|
74
75
|
import apAgingDetail, { initialState as initialApAgingDetailState, } from './view/apAgingView/apAgingDetail/apAgingDetailReducer';
|
|
75
76
|
import apAging, { initialState as initialApAgingState, } from './view/apAgingView/apAgingReport/apAgingReducer';
|
|
76
77
|
import arAgingDetail, { initialState as initialArAgingDetailState, } from './view/arAgingView/arAgingDetail/arAgingDetailReducer';
|
|
@@ -97,6 +98,7 @@ import companyHealthMetricView, { initialState as initialCompanyHealthMetricView
|
|
|
97
98
|
import companyMonthEndReportView, { initialState as initialCompanyMonthEndReportViewState, } from './view/companyMonthEndReportView/companyMonthEndReportViewReducer';
|
|
98
99
|
import companyTaskManagerView, { initialState as initialCompanyTaskManagerViewState, } from './view/companyTaskManagerView/companyTaskManagerViewReducer';
|
|
99
100
|
import companyView, { initialState as initialCompanyViewState, } from './view/companyView/companyViewReducer';
|
|
101
|
+
import creditAgentView, { initialState as initialCreditAgentViewState, } from './view/creditAgentView/creditAgentViewReducer';
|
|
100
102
|
import dashboard, { initialState as initialDashboardState, } from './view/dashboard/dashboardReducer';
|
|
101
103
|
import dashboardLayout, { initialState as initialDashboardLayoutState, } from './view/dashboardLayout/dashboardLayoutReducer';
|
|
102
104
|
import expenseAutomationView, { initialState as initialExpenseAutomationViewState, } from './view/expenseAutomationView/expenseAutomationViewReducer';
|
|
@@ -341,6 +343,8 @@ const initialViewsState = {
|
|
|
341
343
|
companyTaskManagerViewState: initialCompanyTaskManagerViewState,
|
|
342
344
|
companyViewState: initialCompanyViewState,
|
|
343
345
|
countryListState: initialCountryListState,
|
|
346
|
+
creditAgentEntityState: initialCreditAgentEntityState,
|
|
347
|
+
creditAgentViewState: initialCreditAgentViewState,
|
|
344
348
|
dashboardLayoutState: initialDashboardLayoutState,
|
|
345
349
|
dashboardState: initialDashboardState,
|
|
346
350
|
depositAccountListState: initialDepositAccountListState,
|
|
@@ -573,6 +577,8 @@ const viewReducers = {
|
|
|
573
577
|
companyTaskManagerViewState: companyTaskManagerView,
|
|
574
578
|
companyViewState: companyView,
|
|
575
579
|
countryListState: countryList,
|
|
580
|
+
creditAgentEntityState: creditAgentEntity,
|
|
581
|
+
creditAgentViewState: creditAgentView,
|
|
576
582
|
dashboardState: dashboard,
|
|
577
583
|
dashboardLayoutState: dashboardLayout,
|
|
578
584
|
depositAccountListState: depositAccountList,
|
|
@@ -3,11 +3,13 @@ const ALL_RECURRING_FREQUENCY = [
|
|
|
3
3
|
'daily',
|
|
4
4
|
'weekly',
|
|
5
5
|
'biweekly',
|
|
6
|
+
'semi_weekly',
|
|
6
7
|
'monthly',
|
|
7
8
|
'quarterly',
|
|
8
9
|
];
|
|
9
10
|
export const toRecurringFrequency = (v) => stringToUnion(v, ALL_RECURRING_FREQUENCY);
|
|
10
11
|
export const toRecurringFrequencyStrict = (v) => stringToUnionStrict(v ?? '', ALL_RECURRING_FREQUENCY);
|
|
12
|
+
export const SEMI_WEEKLY_REQUIRED_DAYS_COUNT = 2;
|
|
11
13
|
const ALL_RECURRING_DATE_PICKER_OPTIONS = ['date', 'recurringCount'];
|
|
12
14
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13
15
|
const toRecurringDatePickerOptions = (v) => stringToUnion(v, ALL_RECURRING_DATE_PICKER_OPTIONS);
|
|
@@ -15,6 +17,8 @@ export const getRecurringFrequencyInDays = (frequency) => {
|
|
|
15
17
|
switch (frequency) {
|
|
16
18
|
case 'daily':
|
|
17
19
|
return 1;
|
|
20
|
+
case 'semi_weekly':
|
|
21
|
+
return 3;
|
|
18
22
|
case 'weekly':
|
|
19
23
|
return 7;
|
|
20
24
|
case 'biweekly':
|
|
@@ -35,6 +39,17 @@ export const getRecurringEndDateFromCount = (startDate, frequency, recurringCoun
|
|
|
35
39
|
case 'biweekly':
|
|
36
40
|
endDate = startDate.add(getRecurringFrequencyInDays(frequency) * (recurringCount - 1), 'day');
|
|
37
41
|
break;
|
|
42
|
+
case 'semi_weekly': {
|
|
43
|
+
// Two semi-weekly occurrences span exactly 7 days regardless of which
|
|
44
|
+
// two weekdays are picked (since gap1 + gap2 = 7). So odd counts are
|
|
45
|
+
// exact; even counts use the conservative half-week step (4 days).
|
|
46
|
+
const stepsRemaining = recurringCount - 1;
|
|
47
|
+
const fullWeekPairs = Math.floor(stepsRemaining / 2);
|
|
48
|
+
const hasOddTailStep = stepsRemaining % 2 === 1;
|
|
49
|
+
const days = fullWeekPairs * 7 + (hasOddTailStep ? 4 : 0);
|
|
50
|
+
endDate = startDate.add(days, 'day');
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
38
53
|
case 'monthly':
|
|
39
54
|
endDate = startDate.add(recurringCount - 1, 'month');
|
|
40
55
|
break;
|
|
@@ -51,6 +66,8 @@ export const getMinAllowedEndDate = (frequency, startDate) => {
|
|
|
51
66
|
switch (frequency) {
|
|
52
67
|
case 'daily':
|
|
53
68
|
return startDate.add(1, 'day');
|
|
69
|
+
case 'semi_weekly':
|
|
70
|
+
return startDate.add(3, 'day');
|
|
54
71
|
case 'weekly':
|
|
55
72
|
return startDate.add(7, 'day');
|
|
56
73
|
case 'biweekly':
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const CREDIT_REPORT_COLUMNS = [
|
|
2
|
+
'Customer Name',
|
|
3
|
+
'Card Status',
|
|
4
|
+
'Credit Limit ($)',
|
|
5
|
+
'Credit Score',
|
|
6
|
+
'Recommended Amount ($)',
|
|
7
|
+
'Rationale',
|
|
8
|
+
'Report Date',
|
|
9
|
+
'Tenant Email Domain',
|
|
10
|
+
'Tenant ID',
|
|
11
|
+
'Company ID',
|
|
12
|
+
'Updated By',
|
|
13
|
+
'Updated On',
|
|
14
|
+
];
|
|
15
|
+
function rowToFields(row) {
|
|
16
|
+
return [
|
|
17
|
+
row.customerName,
|
|
18
|
+
row.cardStatus,
|
|
19
|
+
row.creditLimit,
|
|
20
|
+
row.creditScore,
|
|
21
|
+
row.recommendedAmount,
|
|
22
|
+
row.rationale,
|
|
23
|
+
row.reportDate?.format('YYYY-MM-DD'),
|
|
24
|
+
row.tenantEmailDomain,
|
|
25
|
+
row.tenantId,
|
|
26
|
+
row.companyId,
|
|
27
|
+
row.updatedByName,
|
|
28
|
+
row.updatedOn?.format('YYYY-MM-DD'),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the payload for the web-app-v2 table download endpoint.
|
|
33
|
+
*
|
|
34
|
+
* The component calls this, then GETs the endpoint with the payload
|
|
35
|
+
* as a query param and handles the blob response as a file download.
|
|
36
|
+
*/
|
|
37
|
+
export function getCreditReportDownloadPayload(rows) {
|
|
38
|
+
return {
|
|
39
|
+
columns: CREDIT_REPORT_COLUMNS,
|
|
40
|
+
rows: rows.map(rowToFields),
|
|
41
|
+
file_name: 'credit-report',
|
|
42
|
+
sheet_name: 'Credit Report',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
const initialTenantView = {
|
|
3
|
+
analyzeStatus: { fetchState: 'Not-Started' },
|
|
4
|
+
updateStatus: { fetchState: 'Not-Started' },
|
|
5
|
+
};
|
|
6
|
+
function getOrCreateTenantView(draft, tenantId) {
|
|
7
|
+
if (draft.tenantViewByTenantId[tenantId] == null) {
|
|
8
|
+
draft.tenantViewByTenantId[tenantId] = {
|
|
9
|
+
...initialTenantView,
|
|
10
|
+
tenantId,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return draft.tenantViewByTenantId[tenantId];
|
|
14
|
+
}
|
|
15
|
+
export const initialCreditAgentViewState = {
|
|
16
|
+
access: {
|
|
17
|
+
fetchStatus: { fetchState: 'Not-Started' },
|
|
18
|
+
isAllowed: false,
|
|
19
|
+
},
|
|
20
|
+
macro: {
|
|
21
|
+
fetchStatus: { fetchState: 'Not-Started' },
|
|
22
|
+
updateStatus: { fetchState: 'Not-Started' },
|
|
23
|
+
},
|
|
24
|
+
profilesFetchStatus: { fetchState: 'Not-Started' },
|
|
25
|
+
tenantViewByTenantId: {},
|
|
26
|
+
uiState: {
|
|
27
|
+
filterText: '',
|
|
28
|
+
focus: 'All',
|
|
29
|
+
minScore: 0,
|
|
30
|
+
sortDir: 'asc',
|
|
31
|
+
sortKey: undefined,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const creditAgentView = createSlice({
|
|
35
|
+
name: 'creditAgentView',
|
|
36
|
+
initialState: initialCreditAgentViewState,
|
|
37
|
+
reducers: {
|
|
38
|
+
// ── Access ──
|
|
39
|
+
fetchCreditAgentAccess(draft) {
|
|
40
|
+
draft.access.fetchStatus = { fetchState: 'In-Progress' };
|
|
41
|
+
},
|
|
42
|
+
fetchCreditAgentAccessSuccess(draft, action) {
|
|
43
|
+
draft.access.fetchStatus = { fetchState: 'Completed' };
|
|
44
|
+
draft.access.isAllowed = action.payload;
|
|
45
|
+
},
|
|
46
|
+
fetchCreditAgentAccessFailure(draft, action) {
|
|
47
|
+
draft.access.fetchStatus = { fetchState: 'Error', error: action.payload };
|
|
48
|
+
},
|
|
49
|
+
// ── Card Profiles (list-level fetch) ──
|
|
50
|
+
fetchCardProfiles(draft) {
|
|
51
|
+
draft.profilesFetchStatus = { fetchState: 'In-Progress' };
|
|
52
|
+
},
|
|
53
|
+
fetchCardProfilesSuccess(draft, action) {
|
|
54
|
+
draft.profilesFetchStatus = { fetchState: 'Completed' };
|
|
55
|
+
// Replace entire tenantViewByTenantId, preserving existing statuses
|
|
56
|
+
// for tenants still present, removing stale ones.
|
|
57
|
+
draft.tenantViewByTenantId = toTenantViewByTenantId(action.payload, draft.tenantViewByTenantId);
|
|
58
|
+
},
|
|
59
|
+
fetchCardProfilesFailure(draft, action) {
|
|
60
|
+
draft.profilesFetchStatus = { fetchState: 'Error', error: action.payload };
|
|
61
|
+
},
|
|
62
|
+
// ── Update Card Profile (per-tenant) ──
|
|
63
|
+
updateCardProfile: {
|
|
64
|
+
reducer(draft, action) {
|
|
65
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
66
|
+
tv.updateStatus = { fetchState: 'In-Progress' };
|
|
67
|
+
},
|
|
68
|
+
prepare(payload) {
|
|
69
|
+
return { payload };
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
updateCardProfileSuccess: {
|
|
73
|
+
reducer(draft, action) {
|
|
74
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
75
|
+
tv.updateStatus = { fetchState: 'Completed' };
|
|
76
|
+
},
|
|
77
|
+
prepare(tenantId) {
|
|
78
|
+
return { payload: { tenantId } };
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
updateCardProfileFailure: {
|
|
82
|
+
reducer(draft, action) {
|
|
83
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
84
|
+
tv.updateStatus = {
|
|
85
|
+
fetchState: 'Error',
|
|
86
|
+
error: action.payload.error,
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
prepare(tenantId, error) {
|
|
90
|
+
return { payload: { tenantId, error } };
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
// ── Analyze / Schedule Cron (per-tenant) ──
|
|
94
|
+
scheduleTenantCreditScoreCron: {
|
|
95
|
+
reducer(draft, action) {
|
|
96
|
+
if (action.payload.tenantId != null) {
|
|
97
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
98
|
+
tv.analyzeStatus = { fetchState: 'In-Progress' };
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
prepare(tenantId) {
|
|
102
|
+
return { payload: { tenantId } };
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
scheduleTenantCreditScoreCronSuccess: {
|
|
106
|
+
reducer(draft, action) {
|
|
107
|
+
if (action.payload.tenantId != null) {
|
|
108
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
109
|
+
tv.analyzeStatus = { fetchState: 'Completed' };
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
prepare(tenantId) {
|
|
113
|
+
return { payload: { tenantId } };
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
scheduleTenantCreditScoreCronFailure: {
|
|
117
|
+
reducer(draft, action) {
|
|
118
|
+
if (action.payload.tenantId != null) {
|
|
119
|
+
const tv = getOrCreateTenantView(draft, action.payload.tenantId);
|
|
120
|
+
tv.analyzeStatus = {
|
|
121
|
+
fetchState: 'Error',
|
|
122
|
+
error: action.payload.error,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
prepare(tenantId, error) {
|
|
127
|
+
return { payload: { tenantId, error } };
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
// ── Macro ──
|
|
131
|
+
fetchCreditAgentMacro(draft) {
|
|
132
|
+
draft.macro.fetchStatus = { fetchState: 'In-Progress' };
|
|
133
|
+
},
|
|
134
|
+
fetchCreditAgentMacroSuccess(draft) {
|
|
135
|
+
draft.macro.fetchStatus = { fetchState: 'Completed' };
|
|
136
|
+
},
|
|
137
|
+
fetchCreditAgentMacroFailure(draft, action) {
|
|
138
|
+
draft.macro.fetchStatus = { fetchState: 'Error', error: action.payload };
|
|
139
|
+
},
|
|
140
|
+
saveCreditAgentMacro: {
|
|
141
|
+
reducer(draft) {
|
|
142
|
+
draft.macro.updateStatus = { fetchState: 'In-Progress' };
|
|
143
|
+
},
|
|
144
|
+
prepare(instructions) {
|
|
145
|
+
return { payload: { instructions } };
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
saveCreditAgentMacroSuccess(draft) {
|
|
149
|
+
draft.macro.updateStatus = { fetchState: 'Completed' };
|
|
150
|
+
},
|
|
151
|
+
saveCreditAgentMacroFailure(draft, action) {
|
|
152
|
+
draft.macro.updateStatus = { fetchState: 'Error', error: action.payload };
|
|
153
|
+
},
|
|
154
|
+
resetSaveMacroState(draft) {
|
|
155
|
+
draft.macro.updateStatus = { fetchState: 'Not-Started' };
|
|
156
|
+
},
|
|
157
|
+
// ── UI State ──
|
|
158
|
+
updateCreditAgentUIState(draft, action) {
|
|
159
|
+
Object.assign(draft.uiState, action.payload);
|
|
160
|
+
},
|
|
161
|
+
// ── Clear ──
|
|
162
|
+
clearCreditAgentView() {
|
|
163
|
+
return initialCreditAgentViewState;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
export const { fetchCreditAgentAccess, fetchCreditAgentAccessSuccess, fetchCreditAgentAccessFailure, scheduleTenantCreditScoreCron, scheduleTenantCreditScoreCronSuccess, scheduleTenantCreditScoreCronFailure, fetchCreditAgentMacro, fetchCreditAgentMacroSuccess, fetchCreditAgentMacroFailure, saveCreditAgentMacro, saveCreditAgentMacroSuccess, saveCreditAgentMacroFailure, resetSaveMacroState, fetchCardProfiles, fetchCardProfilesSuccess, fetchCardProfilesFailure, updateCardProfile, updateCardProfileSuccess, updateCardProfileFailure, updateCreditAgentUIState, clearCreditAgentView, } = creditAgentView.actions;
|
|
168
|
+
export { initialCreditAgentViewState as initialState };
|
|
169
|
+
export default creditAgentView.reducer;
|
|
170
|
+
// ── Helpers ──
|
|
171
|
+
const toTenantViewByTenantId = (rows, existing) => {
|
|
172
|
+
const result = {};
|
|
173
|
+
for (const row of rows) {
|
|
174
|
+
const tenantId = row.tenant_id;
|
|
175
|
+
const existingState = existing[tenantId];
|
|
176
|
+
result[tenantId] = existingState ?? {
|
|
177
|
+
analyzeStatus: { fetchState: 'Not-Started' },
|
|
178
|
+
tenantId,
|
|
179
|
+
updateStatus: { fetchState: 'Not-Started' },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
2
|
+
import orderBy from 'lodash/orderBy';
|
|
3
|
+
export const getCreditAgentView = (state) => state.creditAgentViewState;
|
|
4
|
+
export const getCreditAgentEntity = (state) => state.creditAgentEntityState;
|
|
5
|
+
export const getCreditAgentMacro = createSelector(getCreditAgentView, getCreditAgentEntity, (view, entity) => ({
|
|
6
|
+
...view.macro,
|
|
7
|
+
data: entity.macro,
|
|
8
|
+
}));
|
|
9
|
+
export const getCardProfilesData = createSelector(getCreditAgentView, getCreditAgentEntity, (view, entity) => {
|
|
10
|
+
const { uiState } = view;
|
|
11
|
+
const allRows = Object.values(entity.rowByTenantId).map((row) => {
|
|
12
|
+
const tenantView = view.tenantViewByTenantId[row.tenantId];
|
|
13
|
+
return {
|
|
14
|
+
...row,
|
|
15
|
+
analyzeStatus: tenantView?.analyzeStatus ?? {
|
|
16
|
+
fetchState: 'Not-Started',
|
|
17
|
+
},
|
|
18
|
+
updateStatus: tenantView?.updateStatus ?? { fetchState: 'Not-Started' },
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
let filteredRows = allRows;
|
|
22
|
+
// Text filter
|
|
23
|
+
const trimmedFilterText = uiState.filterText.trim();
|
|
24
|
+
if (trimmedFilterText.length > 0) {
|
|
25
|
+
const query = trimmedFilterText.toLowerCase();
|
|
26
|
+
filteredRows = filteredRows.filter((row) => (row.customerName ?? '').toLowerCase().includes(query) ||
|
|
27
|
+
row.tenantId.toLowerCase().includes(query));
|
|
28
|
+
}
|
|
29
|
+
// Focus filter
|
|
30
|
+
filteredRows = applyFocusFilter(filteredRows, uiState.focus);
|
|
31
|
+
// Min score filter
|
|
32
|
+
if (uiState.minScore > 0) {
|
|
33
|
+
filteredRows = filteredRows.filter((row) => {
|
|
34
|
+
const score = parseFloat(row.creditScore ?? '0');
|
|
35
|
+
return score >= uiState.minScore;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// Sorting
|
|
39
|
+
const sortedRows = getSortedRows(filteredRows, uiState.sortKey, uiState.sortDir);
|
|
40
|
+
return {
|
|
41
|
+
fetchStatus: view.profilesFetchStatus,
|
|
42
|
+
rows: sortedRows,
|
|
43
|
+
uiState,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
// ── Helpers ──
|
|
47
|
+
const applyFocusFilter = (rows, focus) => {
|
|
48
|
+
switch (focus) {
|
|
49
|
+
case 'New only':
|
|
50
|
+
return rows.filter((row) => row.isNew);
|
|
51
|
+
case 'Interested':
|
|
52
|
+
return rows.filter((row) => row.cardStatus === 'Interested');
|
|
53
|
+
case 'Active only':
|
|
54
|
+
return rows.filter((row) => row.cardStatus === 'Active' || row.cardStatus === 'Enabled');
|
|
55
|
+
case 'All':
|
|
56
|
+
default:
|
|
57
|
+
return rows;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const getSortedRows = (rows, sortKey, sortDir) => {
|
|
61
|
+
if (sortKey == null) {
|
|
62
|
+
return rows;
|
|
63
|
+
}
|
|
64
|
+
const direction = sortDir === 'asc' ? 'asc' : 'desc';
|
|
65
|
+
return orderBy(rows, (row) => {
|
|
66
|
+
switch (sortKey) {
|
|
67
|
+
case 'name':
|
|
68
|
+
return (row.customerName ?? '').toLowerCase();
|
|
69
|
+
case 'status':
|
|
70
|
+
return row.cardStatus.toLowerCase();
|
|
71
|
+
case 'creditLimit':
|
|
72
|
+
return row.creditLimit ?? 0;
|
|
73
|
+
case 'creditScore':
|
|
74
|
+
return parseFloat(row.creditScore ?? '0');
|
|
75
|
+
case 'recAmount':
|
|
76
|
+
return row.recommendedAmount ?? 0;
|
|
77
|
+
case 'reportDate':
|
|
78
|
+
return row.reportDate?.valueOf() ?? 0;
|
|
79
|
+
case 'updatedBy':
|
|
80
|
+
return (row.updatedByName ?? '').toLowerCase();
|
|
81
|
+
default:
|
|
82
|
+
return (row.customerName ?? '').toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
}, direction);
|
|
85
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { stringToUnion } from '../../commonStateTypes/stringToUnion';
|
|
2
|
+
const ALL_CREDIT_AGENT_SORT_KEYS = [
|
|
3
|
+
'name',
|
|
4
|
+
'status',
|
|
5
|
+
'creditLimit',
|
|
6
|
+
'creditScore',
|
|
7
|
+
'recAmount',
|
|
8
|
+
'reportDate',
|
|
9
|
+
'updatedBy',
|
|
10
|
+
];
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
12
|
+
const toCreditAgentSortKeyType = (v) => stringToUnion(v, ALL_CREDIT_AGENT_SORT_KEYS);
|
|
13
|
+
const ALL_CREDIT_AGENT_FOCUS_FILTERS = [
|
|
14
|
+
'All',
|
|
15
|
+
'New only',
|
|
16
|
+
'Interested',
|
|
17
|
+
'Active only',
|
|
18
|
+
];
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20
|
+
const toCreditAgentFocusFilterType = (v) => stringToUnion(v, ALL_CREDIT_AGENT_FOCUS_FILTERS);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { from, of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { updateCreditAgentRows } from '../../../entity/creditAgent/creditAgentReducer';
|
|
4
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
|
+
import { fetchCardProfiles, fetchCardProfilesFailure, fetchCardProfilesSuccess, } from '../creditAgentViewReducer';
|
|
6
|
+
export const fetchCardProfilesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardProfiles.match), switchMap(() => {
|
|
7
|
+
const url = `${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/cockpit/card-tenant-profiles`;
|
|
8
|
+
return zeniAPI.getJSON(url).pipe(mergeMap((response) => {
|
|
9
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
10
|
+
const rawRows = response.data.rows;
|
|
11
|
+
return from([
|
|
12
|
+
updateCreditAgentRows(rawRows),
|
|
13
|
+
fetchCardProfilesSuccess(rawRows),
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return of(fetchCardProfilesFailure(response.status));
|
|
18
|
+
}
|
|
19
|
+
}), catchError((error) => of(fetchCardProfilesFailure(createZeniAPIStatus('Unexpected error', 'REST API call errored out: ' + JSON.stringify(error))))));
|
|
20
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
4
|
+
import { fetchCreditAgentAccess, fetchCreditAgentAccessFailure, fetchCreditAgentAccessSuccess, } from '../creditAgentViewReducer';
|
|
5
|
+
export const fetchCreditAgentAccessEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCreditAgentAccess.match), switchMap(() => {
|
|
6
|
+
const url = `${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/cockpit/cards-cockpit-access`;
|
|
7
|
+
return zeniAPI.getJSON(url).pipe(switchMap((response) => {
|
|
8
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
9
|
+
return of(fetchCreditAgentAccessSuccess(response.data.is_allowed === true));
|
|
10
|
+
}
|
|
11
|
+
return of(fetchCreditAgentAccessFailure(response.status));
|
|
12
|
+
}), catchError((error) => of(fetchCreditAgentAccessFailure(createZeniAPIStatus('Unexpected error', 'REST API call errored out: ' + JSON.stringify(error))))));
|
|
13
|
+
}));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { from, of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { updateCreditAgentMacro } from '../../../entity/creditAgent/creditAgentReducer';
|
|
4
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
|
+
import { fetchCreditAgentMacro, fetchCreditAgentMacroFailure, fetchCreditAgentMacroSuccess, } from '../creditAgentViewReducer';
|
|
6
|
+
export const fetchCreditAgentMacroEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCreditAgentMacro.match), switchMap(() => {
|
|
7
|
+
const url = `${zeniAPI.apiEndPoints.aiCfoMicroServiceBaseUrl}/1.0/macros/credit-agent`;
|
|
8
|
+
return zeniAPI.getJSON(url).pipe(mergeMap((response) => {
|
|
9
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
10
|
+
return from([
|
|
11
|
+
updateCreditAgentMacro(response.data),
|
|
12
|
+
fetchCreditAgentMacroSuccess(),
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
return of(fetchCreditAgentMacroFailure(response.status));
|
|
17
|
+
}
|
|
18
|
+
}), catchError((error) => of(fetchCreditAgentMacroFailure(createZeniAPIStatus('Unexpected error', 'REST API call errored out: ' + JSON.stringify(error))))));
|
|
19
|
+
}));
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { from } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { updateCreditAgentMacro } from '../../../entity/creditAgent/creditAgentReducer';
|
|
4
|
+
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
|
+
import { saveCreditAgentMacro, saveCreditAgentMacroFailure, saveCreditAgentMacroSuccess, } from '../creditAgentViewReducer';
|
|
7
|
+
export const saveCreditAgentMacroEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(saveCreditAgentMacro.match), switchMap((action) => {
|
|
8
|
+
const { instructions } = action.payload;
|
|
9
|
+
const existingMacro = state$.value.creditAgentEntityState.macro;
|
|
10
|
+
// Fail fast if macro has not been loaded yet — avoids overwriting server-side
|
|
11
|
+
// name/description fields with empty defaults.
|
|
12
|
+
if (existingMacro == null) {
|
|
13
|
+
return from([
|
|
14
|
+
saveCreditAgentMacroFailure(createZeniAPIStatus('Save failed', 'Cannot save macro: macro data has not been loaded yet.')),
|
|
15
|
+
openSnackbar({
|
|
16
|
+
messageSection: 'credit_agent_save_macro',
|
|
17
|
+
messageText: 'failed',
|
|
18
|
+
type: 'error',
|
|
19
|
+
}),
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
const url = `${zeniAPI.apiEndPoints.aiCfoMicroServiceBaseUrl}/1.0/macros/credit-agent`;
|
|
23
|
+
const body = {
|
|
24
|
+
instructions,
|
|
25
|
+
name: existingMacro.name,
|
|
26
|
+
description: existingMacro.description,
|
|
27
|
+
};
|
|
28
|
+
return zeniAPI.putAndGetJSON(url, body).pipe(mergeMap((response) => {
|
|
29
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
30
|
+
return from([
|
|
31
|
+
updateCreditAgentMacro(response.data),
|
|
32
|
+
saveCreditAgentMacroSuccess(),
|
|
33
|
+
openSnackbar({
|
|
34
|
+
messageSection: 'credit_agent_save_macro',
|
|
35
|
+
messageText: 'success',
|
|
36
|
+
type: 'success',
|
|
37
|
+
}),
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return from([
|
|
42
|
+
saveCreditAgentMacroFailure(response.status),
|
|
43
|
+
openSnackbar({
|
|
44
|
+
messageSection: 'credit_agent_save_macro',
|
|
45
|
+
messageText: 'failed',
|
|
46
|
+
type: 'error',
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
}), catchError((error) => from([
|
|
51
|
+
saveCreditAgentMacroFailure(createZeniAPIStatus('Unexpected error', 'REST API call errored out: ' + JSON.stringify(error))),
|
|
52
|
+
openSnackbar({
|
|
53
|
+
messageSection: 'credit_agent_save_macro',
|
|
54
|
+
messageText: 'failed',
|
|
55
|
+
type: 'error',
|
|
56
|
+
}),
|
|
57
|
+
])));
|
|
58
|
+
}));
|
|
@@ -0,0 +1,39 @@
|
|
|
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 { scheduleTenantCreditScoreCron, scheduleTenantCreditScoreCronFailure, scheduleTenantCreditScoreCronSuccess, } from '../creditAgentViewReducer';
|
|
6
|
+
export const scheduleTenantCreditScoreCronEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(scheduleTenantCreditScoreCron.match), mergeMap((action) => {
|
|
7
|
+
const { tenantId } = action.payload;
|
|
8
|
+
const url = `${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/crons/cards/tenant-credit-score`;
|
|
9
|
+
const body = tenantId != null ? { tenant_id: tenantId } : {};
|
|
10
|
+
return zeniAPI.postAndGetJSON(url, body).pipe(mergeMap((response) => {
|
|
11
|
+
if (isSuccessResponse(response)) {
|
|
12
|
+
return from([
|
|
13
|
+
scheduleTenantCreditScoreCronSuccess(tenantId),
|
|
14
|
+
openSnackbar({
|
|
15
|
+
messageSection: 'credit_agent_schedule_cron',
|
|
16
|
+
messageText: 'success',
|
|
17
|
+
type: 'success',
|
|
18
|
+
}),
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
return from([
|
|
23
|
+
scheduleTenantCreditScoreCronFailure(tenantId, response.status),
|
|
24
|
+
openSnackbar({
|
|
25
|
+
messageSection: 'credit_agent_schedule_cron',
|
|
26
|
+
messageText: 'failed',
|
|
27
|
+
type: 'error',
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
}), catchError((error) => from([
|
|
32
|
+
scheduleTenantCreditScoreCronFailure(tenantId, createZeniAPIStatus('Unexpected error', 'REST API call errored out: ' + JSON.stringify(error))),
|
|
33
|
+
openSnackbar({
|
|
34
|
+
messageSection: 'credit_agent_schedule_cron',
|
|
35
|
+
messageText: 'failed',
|
|
36
|
+
type: 'error',
|
|
37
|
+
}),
|
|
38
|
+
])));
|
|
39
|
+
}));
|