@zeniai/client-epic-state 5.0.81-betaML3 → 5.0.81-betaRR0
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/common/aiCfo/aiCfoSuggestedQuestionsPageContext.d.ts +1 -1
- package/lib/commonStateTypes/viewAndReport/viewAndReport.d.ts +2 -2
- package/lib/commonStateTypes/viewAndReport/viewAndReport.js +0 -2
- package/lib/entity/account/accountSelector.d.ts +0 -9
- package/lib/entity/account/accountSelector.js +1 -14
- package/lib/entity/account/accountState.d.ts +1 -1
- package/lib/entity/accountGroup/accountGroupState.d.ts +1 -1
- package/lib/entity/aiCfo/aiCfoPayload.d.ts +171 -1
- package/lib/entity/aiCfo/aiCfoReducer.d.ts +22 -2
- package/lib/entity/aiCfo/aiCfoReducer.js +291 -4
- package/lib/entity/aiCfo/aiCfoSelector.d.ts +3 -1
- package/lib/entity/aiCfo/aiCfoSelector.js +11 -1
- package/lib/entity/aiCfo/aiCfoState.d.ts +186 -4
- package/lib/entity/aiCfo/aiCfoState.js +23 -1
- package/lib/entity/cardPolicy/cardPolicyPayload.d.ts +277 -0
- package/lib/entity/cardPolicy/cardPolicyPayload.js +143 -0
- package/lib/entity/cardPolicy/cardPolicyReducer.d.ts +19 -0
- package/lib/entity/cardPolicy/cardPolicyReducer.js +256 -0
- package/lib/entity/cardPolicy/cardPolicySelector.d.ts +32 -0
- package/lib/entity/cardPolicy/cardPolicySelector.js +103 -0
- package/lib/entity/cardPolicy/cardPolicyState.d.ts +205 -0
- package/lib/entity/cardPolicy/cardPolicyState.js +14 -0
- package/lib/entity/cardPolicy/extractPolicyDocumentEpic.d.ts +18 -0
- package/lib/entity/cardPolicy/extractPolicyDocumentEpic.js +54 -0
- package/lib/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.d.ts +26 -0
- package/lib/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.js +47 -0
- package/lib/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.d.ts +25 -0
- package/lib/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.js +39 -0
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.d.ts +23 -0
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.js +92 -0
- package/lib/entity/class/classReducer.d.ts +4 -4
- package/lib/entity/class/classState.d.ts +1 -1
- package/lib/entity/forecast/forecastState.d.ts +1 -1
- package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +1 -1
- package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +1 -1
- package/lib/entity/sectionAccountsView/sectionAccountsView.d.ts +1 -1
- package/lib/entity/sectionClassesViewV2/sectionClassesView.d.ts +1 -1
- package/lib/entity/sectionClassesViewV2/sectionClassesViewReducer.d.ts +2 -2
- package/lib/entity/sectionProjectView/sectionProjectView.d.ts +1 -1
- package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +2 -2
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +2 -1
- package/lib/entity/tenant/clearAllEpic.d.ts +6 -3
- package/lib/entity/tenant/clearAllEpic.js +8 -2
- package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +0 -4
- package/lib/entity/transaction/payloadTypes/transactionPayload.js +10 -18
- package/lib/entity/transaction/stateTypes/transaction.d.ts +0 -3
- package/lib/epic.d.ts +11 -3
- package/lib/epic.js +11 -3
- package/lib/esm/commonStateTypes/viewAndReport/viewAndReport.js +0 -2
- package/lib/esm/entity/account/accountSelector.js +0 -11
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +291 -4
- package/lib/esm/entity/aiCfo/aiCfoSelector.js +10 -1
- package/lib/esm/entity/aiCfo/aiCfoState.js +20 -0
- package/lib/esm/entity/cardPolicy/cardPolicyPayload.js +130 -0
- package/lib/esm/entity/cardPolicy/cardPolicyReducer.js +252 -0
- package/lib/esm/entity/cardPolicy/cardPolicySelector.js +79 -0
- package/lib/esm/entity/cardPolicy/cardPolicyState.js +9 -0
- package/lib/esm/entity/cardPolicy/extractPolicyDocumentEpic.js +50 -0
- package/lib/esm/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.js +43 -0
- package/lib/esm/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.js +35 -0
- package/lib/esm/entity/cardPolicy/policyRecommendationFromUploadEpic.js +88 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +2 -1
- package/lib/esm/entity/tenant/clearAllEpic.js +8 -2
- package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +10 -18
- package/lib/esm/epic.js +11 -3
- package/lib/esm/index.js +26 -12
- package/lib/esm/reducer.js +12 -3
- package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +6 -3
- package/lib/esm/view/aiCfoView/epics/createSessionAndSubmitEpic.js +5 -2
- package/lib/esm/view/aiCfoView/epics/createSessionEpic.js +2 -1
- package/lib/esm/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +1 -13
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -9
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.js +70 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.js +22 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.js +1 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.js +56 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.js +49 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.js +42 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.js +72 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.js +21 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.js +1 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.js +56 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +62 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +39 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.js +1 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.js +53 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.js +25 -0
- package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +25 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.js +2 -2
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +16 -4
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +38 -0
- package/lib/esm/view/transactionDetail/transactionDetailSelector.js +1 -6
- package/lib/index.d.ts +24 -12
- package/lib/index.js +147 -43
- package/lib/reducer.d.ts +12 -3
- package/lib/reducer.js +12 -3
- package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +11 -2
- package/lib/view/aiCfoView/aiCfoViewReducer.js +6 -3
- package/lib/view/aiCfoView/epics/createSessionAndSubmitEpic.js +5 -2
- package/lib/view/aiCfoView/epics/createSessionEpic.js +2 -1
- package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.d.ts +8 -1
- package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
- package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
- package/lib/view/billPayCard/billPayCardSelector.d.ts +1 -1
- package/lib/view/cardBalance/cardBalanceSelector.d.ts +1 -1
- package/lib/view/cashBalance/cashBalanceSelector.d.ts +1 -1
- package/lib/view/cashInCashOut/cashInCashOutSelector.d.ts +1 -1
- package/lib/view/cashPosition/cashPositionSelector.d.ts +1 -1
- package/lib/view/dashboard/dashboardReducer.d.ts +2 -2
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +1 -3
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +2 -14
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +1 -4
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +0 -1
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -11
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +1 -1
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +2 -2
- package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -1
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +1 -1
- package/lib/view/financeStatement/financeStatementReducer.d.ts +1 -1
- package/lib/view/globalMerchantView/globalMerchantViewReducer.d.ts +2 -2
- package/lib/view/people/peopleTypes.d.ts +1 -1
- package/lib/view/reimbursementCard/reimbursementCardSelector.d.ts +1 -1
- package/lib/view/reportsResync/reportsResyncReducer.d.ts +2 -2
- package/lib/view/scheduleView/scheduleListView/scheduleListTypes.d.ts +1 -1
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.d.ts +14 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.js +74 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.d.ts +21 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.js +28 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.d.ts +25 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.js +2 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.d.ts +19 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.js +60 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.d.ts +18 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.js +53 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.d.ts +16 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.js +46 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.d.ts +15 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.js +76 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.d.ts +21 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.js +27 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.d.ts +19 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.js +2 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.d.ts +19 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.js +60 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.d.ts +13 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +66 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.d.ts +32 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +46 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.d.ts +45 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.js +2 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.d.ts +25 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.js +57 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.d.ts +15 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.js +29 -0
- package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.d.ts +1 -1
- package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.d.ts +14 -0
- package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +27 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.d.ts +2 -2
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.js +1 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.d.ts +5 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +17 -5
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.d.ts +27 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +43 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardState.d.ts +2 -0
- package/lib/view/taskManager/taskListView/taskListReducer.d.ts +1 -1
- package/lib/view/tasksCard/tasksCardSelector.d.ts +1 -1
- package/lib/view/topEx/topExSelector.d.ts +1 -1
- package/lib/view/transactionDetail/transactionDetailPayload.d.ts +1 -1
- package/lib/view/transactionDetail/transactionDetailSelector.d.ts +1 -6
- package/lib/view/transactionDetail/transactionDetailSelector.js +1 -6
- package/package.json +1 -1
- package/lib/esm/view/createTransferEntry/createTransferEntryReducer.js +0 -82
- package/lib/esm/view/createTransferEntry/createTransferEntrySelector.js +0 -64
- package/lib/esm/view/createTransferEntry/createTransferEntryState.js +0 -17
- package/lib/esm/view/createTransferEntry/epics/createTransferEntryEpic.js +0 -130
- package/lib/esm/view/createTransferEntry/epics/fetchTransferAccountsEpic.js +0 -37
- package/lib/view/createTransferEntry/createTransferEntryReducer.d.ts +0 -32
- package/lib/view/createTransferEntry/createTransferEntryReducer.js +0 -86
- package/lib/view/createTransferEntry/createTransferEntrySelector.d.ts +0 -39
- package/lib/view/createTransferEntry/createTransferEntrySelector.js +0 -69
- package/lib/view/createTransferEntry/createTransferEntryState.d.ts +0 -30
- package/lib/view/createTransferEntry/createTransferEntryState.js +0 -20
- package/lib/view/createTransferEntry/epics/createTransferEntryEpic.d.ts +0 -11
- package/lib/view/createTransferEntry/epics/createTransferEntryEpic.js +0 -134
- package/lib/view/createTransferEntry/epics/fetchTransferAccountsEpic.d.ts +0 -14
- package/lib/view/createTransferEntry/epics/fetchTransferAccountsEpic.js +0 -41
|
@@ -51,17 +51,6 @@ export const getAccountBase = (accountState, accountId, reportId) => {
|
|
|
51
51
|
reconciliationSource: account.reconciliationSource,
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
|
-
/**
|
|
55
|
-
* Transfer-account IDs from the API are resolved under `transfer_accounts`; if that
|
|
56
|
-
* slice is not hydrated yet, fall back to `account_list` so dropdowns still populate.
|
|
57
|
-
*/
|
|
58
|
-
export const getAccountBaseForTransferAccounts = (accountState, accountId) => getAccountBase(accountState, accountId, 'transfer_accounts') ??
|
|
59
|
-
getAccountBase(accountState, accountId, 'account_list');
|
|
60
|
-
/**
|
|
61
|
-
* Credit-card-only pool from the transfer-flow accounts API; falls back to `account_list`.
|
|
62
|
-
*/
|
|
63
|
-
export const getAccountBaseForCreditCardTransferAccounts = (accountState, accountId) => getAccountBase(accountState, accountId, 'credit_card_transfer_accounts') ??
|
|
64
|
-
getAccountBase(accountState, accountId, 'account_list');
|
|
65
54
|
export function getAccount(accountState, id, filter, additionalBalancesOptions) {
|
|
66
55
|
const key = getAccountKey(id.reportId, id.accountId, id.classesViewParentId, id.accountsViewParentId, id.projectsViewParentId);
|
|
67
56
|
const account = accountState.accountsByKey[key];
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { date as zeniDate } from '../../zeniDayJS';
|
|
3
|
-
import { ALL_AI_CFO_ANSWER_RESPONSE_TYPES, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toAiCfoAnswerStateType, toAiCfoVisualizationTypeStrict, toMessageSender, toMessageType, toYFormatScaleStrict, toYFormatTypeStrict, toYFormatUnitStrict, } from './aiCfoState';
|
|
3
|
+
import { ALL_AI_CFO_ANSWER_RESPONSE_TYPES, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toAiCfoAnswerStateType, toAiCfoVisualizationTypeStrict, toInteractiveFormTypeStrict, toMessageSender, toMessageType, toYFormatScaleStrict, toYFormatTypeStrict, toYFormatUnitStrict, } from './aiCfoState';
|
|
4
4
|
export const initialAiCfoState = {
|
|
5
5
|
aiCfoByChatSessionId: {},
|
|
6
6
|
partialQuestionAnswers: {},
|
|
7
|
+
syntheticAnswersByChatSessionId: {},
|
|
7
8
|
};
|
|
8
9
|
// Utility: Merge two arrays of Q&A pairs, combining responses for the same question
|
|
9
10
|
function mergeQuestionAnswerPairs(existing, newQAs) {
|
|
@@ -135,9 +136,185 @@ export const toAiCfoVisualization = (visualization) => {
|
|
|
135
136
|
if (visualization.type == null || visualization.data == null) {
|
|
136
137
|
return undefined;
|
|
137
138
|
}
|
|
139
|
+
const normalizedType = toAiCfoVisualizationTypeStrict(visualization.type) ?? 'table';
|
|
138
140
|
return {
|
|
139
|
-
type:
|
|
140
|
-
data:
|
|
141
|
+
type: normalizedType,
|
|
142
|
+
data: normalizedType === 'interactive_form'
|
|
143
|
+
? toInteractiveFormVisualization(visualization.data)
|
|
144
|
+
: toAiCfoVisualizationData(visualization.data),
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
const toInteractiveFormVisualization = (data) => {
|
|
148
|
+
const formType = toInteractiveFormTypeStrict(data.form_type) ?? 'cards_creation';
|
|
149
|
+
if (formType === 'card_policy') {
|
|
150
|
+
return {
|
|
151
|
+
formType: 'card_policy',
|
|
152
|
+
payload: toCardPolicyInitialData(data.payload),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
formType: 'cards_creation',
|
|
157
|
+
payload: toCardsCreationInitialData(data.payload),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
const toCardsCreationInitialData = (payload) => {
|
|
161
|
+
const cardOptions = (payload.card_options ?? []).map((option) => ({
|
|
162
|
+
id: option.id,
|
|
163
|
+
label: option.label,
|
|
164
|
+
description: option.description ?? undefined,
|
|
165
|
+
}));
|
|
166
|
+
if (payload.mode === 'create') {
|
|
167
|
+
return {
|
|
168
|
+
mode: 'create',
|
|
169
|
+
cardOptions,
|
|
170
|
+
cardRows: (payload.card_rows ?? []).map((row) => ({
|
|
171
|
+
cardOptionId: row.card_option_id,
|
|
172
|
+
id: row.id,
|
|
173
|
+
label: row.label ?? undefined,
|
|
174
|
+
recommendedLimit: row.recommended_limit ?? undefined,
|
|
175
|
+
suggestedOwnerUserId: row.suggested_owner_user_id ?? undefined,
|
|
176
|
+
})),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// Preferred wire format: a flat `suggested_cards` array with an
|
|
180
|
+
// `is_suggested` discriminator. The adapter splits it into seed rows,
|
|
181
|
+
// owner map, and the full dropdown universe.
|
|
182
|
+
if (payload.suggested_cards != null) {
|
|
183
|
+
return fromSuggestedCards(payload.suggested_cards, cardOptions);
|
|
184
|
+
}
|
|
185
|
+
// Legacy / test shape: backend already pre-shapes seeds. Synthesize an
|
|
186
|
+
// empty `cardNameOptions` (step 2 dropdown still works via row-self ids).
|
|
187
|
+
const teamRows = (payload.team_rows ?? []).map((row) => ({
|
|
188
|
+
avgMonthlySpend: row.avg_monthly_spend,
|
|
189
|
+
cardKind: (row.card_kind ?? 'vendor'),
|
|
190
|
+
id: row.id,
|
|
191
|
+
label: row.label,
|
|
192
|
+
recommendedLimit: row.recommended_limit,
|
|
193
|
+
recommendedLimitCadence: row.recommended_limit_cadence ?? undefined,
|
|
194
|
+
}));
|
|
195
|
+
return {
|
|
196
|
+
mode: 'review',
|
|
197
|
+
cardOptions,
|
|
198
|
+
cardNameOptions: teamRows.map((row) => ({
|
|
199
|
+
id: row.id,
|
|
200
|
+
kind: row.cardKind,
|
|
201
|
+
label: row.label,
|
|
202
|
+
})),
|
|
203
|
+
suggestedOwnerUserIdByTeamId: Object.fromEntries(Object.entries(payload.suggested_owner_user_id_by_team_id ?? {}).map(([teamId, userId]) => [teamId, userId ?? undefined])),
|
|
204
|
+
teamRows,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', {
|
|
208
|
+
style: 'currency',
|
|
209
|
+
currency: 'USD',
|
|
210
|
+
maximumFractionDigits: 0,
|
|
211
|
+
});
|
|
212
|
+
const fromSuggestedCards = (suggestedCards, cardOptions) => {
|
|
213
|
+
const ordered = [...suggestedCards].sort((a, b) => {
|
|
214
|
+
if (a.card_type === b.card_type) {
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
return a.card_type === 'vendor' ? -1 : 1;
|
|
218
|
+
});
|
|
219
|
+
const withUniqueIds = withSynthesizedIds(ordered);
|
|
220
|
+
const teamRows = withUniqueIds
|
|
221
|
+
.filter(({ card }) => card.is_suggested)
|
|
222
|
+
.map(({ card, uniqueId }) => ({
|
|
223
|
+
avgMonthlySpend: CURRENCY_FORMATTER.format(card.avg_spend),
|
|
224
|
+
cardKind: card.card_type === 'department'
|
|
225
|
+
? 'class'
|
|
226
|
+
: 'vendor',
|
|
227
|
+
id: uniqueId,
|
|
228
|
+
label: card.card_name,
|
|
229
|
+
recommendedLimit: CURRENCY_FORMATTER.format(card.credit_limit),
|
|
230
|
+
recommendedLimitCadence: 'Monthly',
|
|
231
|
+
}));
|
|
232
|
+
const suggestedOwnerUserIdByTeamId = Object.fromEntries(withUniqueIds
|
|
233
|
+
.filter(({ card }) => card.is_suggested)
|
|
234
|
+
.map(({ card, uniqueId }) => [uniqueId, card.owner_id]));
|
|
235
|
+
const cardNameOptions = withUniqueIds.map(({ card, uniqueId }) => ({
|
|
236
|
+
id: uniqueId,
|
|
237
|
+
kind: card.card_type === 'department'
|
|
238
|
+
? 'class'
|
|
239
|
+
: 'vendor',
|
|
240
|
+
label: card.card_name,
|
|
241
|
+
}));
|
|
242
|
+
return {
|
|
243
|
+
mode: 'review',
|
|
244
|
+
cardOptions,
|
|
245
|
+
cardNameOptions,
|
|
246
|
+
suggestedOwnerUserIdByTeamId,
|
|
247
|
+
teamRows,
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Stamp each suggested card with a stable unique id derived from its
|
|
252
|
+
* `card_name_id`. If two cards in the same payload share the same
|
|
253
|
+
* `card_name_id` (which happens in the current sample payload where
|
|
254
|
+
* `card_name_id` is the literal `"vendor_uuid"`), fall back to a
|
|
255
|
+
* synthesized id `${kind}__${card_name_id}__${index}` so RHF row keying
|
|
256
|
+
* stays unique.
|
|
257
|
+
*/
|
|
258
|
+
const withSynthesizedIds = (cards) => {
|
|
259
|
+
const seen = new Set();
|
|
260
|
+
return cards.map((card, index) => {
|
|
261
|
+
const preferred = card.card_name_id;
|
|
262
|
+
if (preferred != null && preferred !== '' && !seen.has(preferred)) {
|
|
263
|
+
seen.add(preferred);
|
|
264
|
+
return { card, uniqueId: preferred };
|
|
265
|
+
}
|
|
266
|
+
const synthesized = `${card.card_type}__${preferred ?? 'unknown'}__${index}`;
|
|
267
|
+
seen.add(synthesized);
|
|
268
|
+
return { card, uniqueId: synthesized };
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
const toCardPolicyDraftPolicy = (payload, index) => ({
|
|
272
|
+
id: `draft-${index}`,
|
|
273
|
+
name: payload.name,
|
|
274
|
+
description: payload.description ?? '',
|
|
275
|
+
mode: payload.mode,
|
|
276
|
+
allowedEntity: {
|
|
277
|
+
categoryCodes: payload.allowed_entity?.category_codes ?? [],
|
|
278
|
+
merchantNames: payload.allowed_entity?.merchant_names ?? [],
|
|
279
|
+
},
|
|
280
|
+
blockedEntity: {
|
|
281
|
+
categoryCodes: payload.blocked_entity?.category_codes ?? [],
|
|
282
|
+
merchantNames: payload.blocked_entity?.merchant_names ?? [],
|
|
283
|
+
},
|
|
284
|
+
applyToCards: payload.apply_to_cards ?? [],
|
|
285
|
+
requiredReceiptThreshold: payload.required_receipt_threshold ?? 0,
|
|
286
|
+
spendLimits: {
|
|
287
|
+
transaction: payload.spend_limits?.transaction ?? 0,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
const toCardPolicyStep5Review = (payload) => ({
|
|
291
|
+
draftPolicies: (payload.draft_policies ?? []).map(toCardPolicyDraftPolicy),
|
|
292
|
+
groupingSource: payload.grouping_source ?? 'none',
|
|
293
|
+
prompt: payload.prompt ?? '',
|
|
294
|
+
});
|
|
295
|
+
const toCardPolicyWizardPlan = (payload) => ({
|
|
296
|
+
step5Review: toCardPolicyStep5Review(payload.step_5_review),
|
|
297
|
+
});
|
|
298
|
+
const toCardPolicyUploadSource = (payload) => ({
|
|
299
|
+
confidenceScore: payload.confidence_score,
|
|
300
|
+
fileName: payload.file_name,
|
|
301
|
+
lowConfidenceFields: payload.low_confidence_fields ?? [],
|
|
302
|
+
});
|
|
303
|
+
const toCardPolicyInitialData = (payload) => {
|
|
304
|
+
if (payload.mode === 'upload') {
|
|
305
|
+
return {
|
|
306
|
+
policyKind: 'upload',
|
|
307
|
+
wizardPlan: payload.wizard_plan != null
|
|
308
|
+
? toCardPolicyWizardPlan(payload.wizard_plan)
|
|
309
|
+
: undefined,
|
|
310
|
+
source: payload.source != null
|
|
311
|
+
? toCardPolicyUploadSource(payload.source)
|
|
312
|
+
: undefined,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
policyKind: payload.mode,
|
|
317
|
+
wizardPlan: toCardPolicyWizardPlan(payload.wizard_plan),
|
|
141
318
|
};
|
|
142
319
|
};
|
|
143
320
|
const toAiCfoVisualizationData = (data) => {
|
|
@@ -453,9 +630,118 @@ const aiCfo = createSlice({
|
|
|
453
630
|
session.questionAnswers.push(qa);
|
|
454
631
|
}
|
|
455
632
|
},
|
|
633
|
+
/**
|
|
634
|
+
* Patch the `card_policy` interactive-form payload for a specific
|
|
635
|
+
* answer in a chat session to merge in the wizard plan + upload
|
|
636
|
+
* source metadata returned by
|
|
637
|
+
* `POST /cards/1.0/ai-cfo/policy-recommendation-from-upload`.
|
|
638
|
+
*
|
|
639
|
+
* Dispatched by the recommendation epic after a successful response;
|
|
640
|
+
* the wizard re-renders Steps 2 + 3 of the upload variant once the
|
|
641
|
+
* payload becomes a populated `CardPolicyInitialData` with
|
|
642
|
+
* `policyKind: "upload"` + `wizardPlan` + `source`.
|
|
643
|
+
*
|
|
644
|
+
* No-op when the target answer / chat session / form payload is
|
|
645
|
+
* missing or isn't an upload-mode card-policy interactive form —
|
|
646
|
+
* the bridge epic is best-effort and silently drops in edge cases
|
|
647
|
+
* (e.g. the user closed the chat before recommendation returned).
|
|
648
|
+
*/
|
|
649
|
+
updateAiCfoAnswerCardPolicyWizardPlan(draft, action) {
|
|
650
|
+
const { answerId, chatSessionId, source, wizardPlan } = action.payload;
|
|
651
|
+
const session = draft.aiCfoByChatSessionId[chatSessionId];
|
|
652
|
+
if (session == null) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const qa = session.questionAnswers.find((entry) => entry.questionAnswerId === answerId);
|
|
656
|
+
if (qa == null) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
for (const response of Object.values(qa.responses)) {
|
|
660
|
+
const visualization = response?.content.visualization;
|
|
661
|
+
if (visualization == null ||
|
|
662
|
+
visualization.type !== 'interactive_form') {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
const data = visualization.data;
|
|
666
|
+
if (data.formType !== 'card_policy' ||
|
|
667
|
+
data.payload.policyKind !== 'upload') {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
data.payload = {
|
|
671
|
+
...data.payload,
|
|
672
|
+
wizardPlan,
|
|
673
|
+
source: source ?? data.payload.source,
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
/**
|
|
678
|
+
* Append a client-synthesized answer bubble to a chat session. Used by
|
|
679
|
+
* the AI CFO cards/policy create flow to render a "Creating…" placeholder
|
|
680
|
+
* in the drawer while the underlying mutation runs.
|
|
681
|
+
*
|
|
682
|
+
* The caller is responsible for generating a stable `id` (so the same
|
|
683
|
+
* bubble can later be updated in place via `updateSyntheticAiCfoAnswer`
|
|
684
|
+
* once the mutation completes) and a `createdAt` timestamp so the
|
|
685
|
+
* renderer can interleave synthetic bubbles with streamed Q&A pairs in
|
|
686
|
+
* chronological order.
|
|
687
|
+
*/
|
|
688
|
+
appendSyntheticAiCfoAnswer(draft, action) {
|
|
689
|
+
const { chatSessionId, id, kind, createdAt, entityIds } = action.payload;
|
|
690
|
+
if (draft.syntheticAnswersByChatSessionId[chatSessionId] == null) {
|
|
691
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId] = [];
|
|
692
|
+
}
|
|
693
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId].push({
|
|
694
|
+
id,
|
|
695
|
+
kind,
|
|
696
|
+
entityIds: entityIds ?? [],
|
|
697
|
+
createdAt,
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
/**
|
|
701
|
+
* Update an existing synthetic answer bubble in place. Typically called
|
|
702
|
+
* when the create mutation completes: the screen transitions the bubble
|
|
703
|
+
* from `creating_*` to `created_*` and populates `entityIds` with the
|
|
704
|
+
* newly created cards / policy templates.
|
|
705
|
+
*
|
|
706
|
+
* No-op when the target bubble is missing (e.g. the session was cleared
|
|
707
|
+
* before the mutation returned).
|
|
708
|
+
*/
|
|
709
|
+
updateSyntheticAiCfoAnswer(draft, action) {
|
|
710
|
+
const { chatSessionId, id, kind, entityIds } = action.payload;
|
|
711
|
+
const list = draft.syntheticAnswersByChatSessionId[chatSessionId];
|
|
712
|
+
if (list == null) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const entry = list.find((item) => item.id === id);
|
|
716
|
+
if (entry == null) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
entry.kind = kind;
|
|
720
|
+
if (entityIds != null) {
|
|
721
|
+
entry.entityIds = entityIds;
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
/**
|
|
725
|
+
* Remove a single synthetic answer bubble (e.g. when the user manually
|
|
726
|
+
* dismisses it or the mutation errors out and we want to fall back to
|
|
727
|
+
* an inline error snackbar).
|
|
728
|
+
*/
|
|
729
|
+
removeSyntheticAiCfoAnswer(draft, action) {
|
|
730
|
+
const { chatSessionId, id } = action.payload;
|
|
731
|
+
const list = draft.syntheticAnswersByChatSessionId[chatSessionId];
|
|
732
|
+
if (list == null) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId] = list.filter((item) => item.id !== id);
|
|
736
|
+
},
|
|
737
|
+
/** Clear every synthetic bubble for a session. */
|
|
738
|
+
clearSyntheticAiCfoAnswers(draft, action) {
|
|
739
|
+
delete draft.syntheticAnswersByChatSessionId[action.payload];
|
|
740
|
+
},
|
|
456
741
|
clearSession(draft, action) {
|
|
457
742
|
const sessionId = action.payload;
|
|
458
743
|
delete draft.aiCfoByChatSessionId[sessionId];
|
|
744
|
+
delete draft.syntheticAnswersByChatSessionId[sessionId];
|
|
459
745
|
// Clear partial Q&A if it belongs to the cleared session
|
|
460
746
|
if (draft.partialQuestionAnswers?.[sessionId] != null) {
|
|
461
747
|
draft.partialQuestionAnswers[sessionId] = undefined;
|
|
@@ -464,6 +750,7 @@ const aiCfo = createSlice({
|
|
|
464
750
|
clearAiCfo(draft) {
|
|
465
751
|
draft.aiCfoByChatSessionId = {};
|
|
466
752
|
draft.partialQuestionAnswers = {};
|
|
753
|
+
draft.syntheticAnswersByChatSessionId = {};
|
|
467
754
|
},
|
|
468
755
|
deleteQuestion(draft, action) {
|
|
469
756
|
const { chatSessionId, questionAnswerId } = action.payload;
|
|
@@ -488,5 +775,5 @@ const toChatSession = (chatSessionPayload) => {
|
|
|
488
775
|
createdAt: zeniDate(created_at),
|
|
489
776
|
};
|
|
490
777
|
};
|
|
491
|
-
export const { setNewSession, clearSession, setSessions, setChatHistory, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearAiCfo, deleteQuestion, } = aiCfo.actions;
|
|
778
|
+
export const { setNewSession, clearSession, setSessions, setChatHistory, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearAiCfo, deleteQuestion, updateAiCfoAnswerCardPolicyWizardPlan, appendSyntheticAiCfoAnswer, updateSyntheticAiCfoAnswer, removeSyntheticAiCfoAnswer, clearSyntheticAiCfoAnswers, } = aiCfo.actions;
|
|
492
779
|
export default aiCfo.reducer;
|
|
@@ -32,5 +32,14 @@ export function getAiCfoSelectorView(state) {
|
|
|
32
32
|
: [],
|
|
33
33
|
}))
|
|
34
34
|
.sort((a, b) => b.chatSession.createdAt.valueOf() - a.chatSession.createdAt.valueOf());
|
|
35
|
-
return {
|
|
35
|
+
return {
|
|
36
|
+
allSessionsWithOrderedQuestionAnswers,
|
|
37
|
+
syntheticAnswersByChatSessionId: state.syntheticAnswersByChatSessionId ?? {},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function getSyntheticAiCfoAnswersForChatSession(state, chatSessionId) {
|
|
41
|
+
const arr = state.syntheticAnswersByChatSessionId?.[chatSessionId] ?? [];
|
|
42
|
+
return arr
|
|
43
|
+
.slice()
|
|
44
|
+
.sort((a, b) => a.createdAt.valueOf() - b.createdAt.valueOf());
|
|
36
45
|
}
|
|
@@ -7,7 +7,14 @@ export const ALL_AI_CFO_CHARTS_TYPES = [
|
|
|
7
7
|
export const ALL_AI_CFO_VISUALIZATION_TYPES = [
|
|
8
8
|
...ALL_AI_CFO_CHARTS_TYPES,
|
|
9
9
|
'table',
|
|
10
|
+
'interactive_form',
|
|
10
11
|
];
|
|
12
|
+
export const ALL_INTERACTIVE_FORM_TYPES = [
|
|
13
|
+
'cards_creation',
|
|
14
|
+
'card_policy',
|
|
15
|
+
];
|
|
16
|
+
export const toInteractiveFormType = (v) => stringToUnion(v, ALL_INTERACTIVE_FORM_TYPES);
|
|
17
|
+
export const toInteractiveFormTypeStrict = (v) => stringToUnionStrict(v, ALL_INTERACTIVE_FORM_TYPES);
|
|
11
18
|
export const toAiCfoVisualizationType = (v) => stringToUnion(v, ALL_AI_CFO_VISUALIZATION_TYPES);
|
|
12
19
|
export const toAiCfoVisualizationTypeStrict = (v) => stringToUnionStrict(v, ALL_AI_CFO_VISUALIZATION_TYPES);
|
|
13
20
|
export const toAiCfoChartType = (v) => stringToUnion(v, ALL_AI_CFO_CHARTS_TYPES);
|
|
@@ -58,3 +65,16 @@ export const toMessageSender = (v) => stringToUnion(v, ALL_MESSAGE_SENDERS);
|
|
|
58
65
|
export const toMessageType = (v) => stringToUnion(v, ALL_MESSAGE_TYPES);
|
|
59
66
|
export const toMessageSenderStrict = (v) => stringToUnionStrict(v, ALL_MESSAGE_SENDERS);
|
|
60
67
|
export const toMessageTypeStrict = (v) => stringToUnionStrict(v, ALL_MESSAGE_TYPES);
|
|
68
|
+
/**
|
|
69
|
+
* Discriminator for client-synthesized AI CFO answer bubbles. These are not
|
|
70
|
+
* Q/A pairs streamed from the backend — they are pushed locally by the AI
|
|
71
|
+
* CFO flow when it kicks off a cards or policy create from an interactive
|
|
72
|
+
* form, so the drawer can render a "Creating…" placeholder and later a
|
|
73
|
+
* "Created" success bubble while the corresponding mutation epic runs.
|
|
74
|
+
*/
|
|
75
|
+
export const ALL_SYNTHETIC_AI_CFO_ANSWER_KINDS = [
|
|
76
|
+
'creating_cards',
|
|
77
|
+
'creating_policy',
|
|
78
|
+
'created_cards',
|
|
79
|
+
'created_policy',
|
|
80
|
+
];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { date } from '../../zeniDayJS';
|
|
2
|
+
import { toCardPolicyTemplateMode, toCardPolicyTemplateStatus, } from './cardPolicyState';
|
|
3
|
+
export const toMccCategory = (payload) => ({
|
|
4
|
+
count: payload.count,
|
|
5
|
+
mccCodes: payload.mcc_codes ?? [],
|
|
6
|
+
name: payload.name,
|
|
7
|
+
});
|
|
8
|
+
export const toCardPolicyVendorSearchOption = (payload) => ({
|
|
9
|
+
vendorId: payload.vendor_id,
|
|
10
|
+
name: payload.vendor_name,
|
|
11
|
+
});
|
|
12
|
+
export const toCardPolicyStats = (payload) => ({
|
|
13
|
+
activePolicies: payload.active_policies,
|
|
14
|
+
totalCards: payload.total_cards,
|
|
15
|
+
totalTemplates: payload.total_templates,
|
|
16
|
+
});
|
|
17
|
+
export const toCardPolicyTemplateList = (data) => (data.templates ?? []).map((row) => toCardPolicyTemplate(row.data.template));
|
|
18
|
+
const toCardPolicyTemplateEntityList = (payload) => ({
|
|
19
|
+
categoryCodes: payload.category_codes ?? [],
|
|
20
|
+
merchantNames: payload.merchant_names ?? [],
|
|
21
|
+
});
|
|
22
|
+
export const toCardPolicyTemplate = (payload) => ({
|
|
23
|
+
allowedEntity: toCardPolicyTemplateEntityList(payload.allowed_entity),
|
|
24
|
+
appliedCards: payload.applied_cards ?? [],
|
|
25
|
+
blockedEntity: toCardPolicyTemplateEntityList(payload.blocked_entity),
|
|
26
|
+
cardsCount: payload.cards_count,
|
|
27
|
+
categoryRestrictions: payload.category_restrictions ?? [],
|
|
28
|
+
createdAt: date(payload.created_at),
|
|
29
|
+
createdBy: payload.created_by,
|
|
30
|
+
description: payload.description,
|
|
31
|
+
mode: toCardPolicyTemplateMode(payload.mode),
|
|
32
|
+
name: payload.name,
|
|
33
|
+
requiredReceiptThreshold: payload.required_receipt_threshold,
|
|
34
|
+
spendLimits: {
|
|
35
|
+
transaction: payload.spend_limits.transaction,
|
|
36
|
+
},
|
|
37
|
+
status: toCardPolicyTemplateStatus(payload.status),
|
|
38
|
+
templateId: payload.template_id,
|
|
39
|
+
tenantId: payload.tenant_id,
|
|
40
|
+
updatedAt: date(payload.updated_at),
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Re-shape a single camelCase create request into the snake_case wire
|
|
44
|
+
* body. Used by the bulk-create mapper to build each entry of the
|
|
45
|
+
* `templates` array, and directly by the update epic (PUT) since the
|
|
46
|
+
* update endpoint still consumes a single object.
|
|
47
|
+
*/
|
|
48
|
+
export const toCreateCardPolicyTemplateRequestBody = (request) => ({
|
|
49
|
+
allowed_entity: {
|
|
50
|
+
category_codes: request.allowedEntity.categoryCodes,
|
|
51
|
+
merchant_names: request.allowedEntity.merchantNames,
|
|
52
|
+
},
|
|
53
|
+
apply_to_cards: request.applyToCards,
|
|
54
|
+
blocked_entity: {
|
|
55
|
+
category_codes: request.blockedEntity.categoryCodes,
|
|
56
|
+
merchant_names: request.blockedEntity.merchantNames,
|
|
57
|
+
},
|
|
58
|
+
description: request.description,
|
|
59
|
+
mode: request.mode ?? 'strict',
|
|
60
|
+
name: request.name,
|
|
61
|
+
required_receipt_threshold: request.requiredReceiptThreshold,
|
|
62
|
+
spend_limits: {
|
|
63
|
+
transaction: request.spendLimits.transaction,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Re-shape a bulk create request into the snake_case wire body for
|
|
68
|
+
* `POST /cards/1.0/policy-templates`. Maps each entry through
|
|
69
|
+
* `toCreateCardPolicyTemplateRequestBody` and only emits
|
|
70
|
+
* `source_chat_session_id` when the camelCase counterpart is defined,
|
|
71
|
+
* so manual-create callers don't ship a stray `undefined` over the wire.
|
|
72
|
+
*/
|
|
73
|
+
export const toCreateCardPolicyTemplatesRequestBody = (request) => {
|
|
74
|
+
const body = {
|
|
75
|
+
templates: request.templates.map(toCreateCardPolicyTemplateRequestBody),
|
|
76
|
+
};
|
|
77
|
+
if (request.sourceChatSessionId != null) {
|
|
78
|
+
body.source_chat_session_id = request.sourceChatSessionId;
|
|
79
|
+
}
|
|
80
|
+
return body;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Map a single bulk-response error payload to its camelCased view shape.
|
|
84
|
+
* Defensive against missing optional fields (see `BulkCreateCardPolicyTemplateErrorPayload`
|
|
85
|
+
* docstring — the BE contract isn't fully locked yet).
|
|
86
|
+
*/
|
|
87
|
+
export const toBulkCreateCardPolicyTemplateError = (payload) => ({
|
|
88
|
+
message: payload.message,
|
|
89
|
+
code: payload.code,
|
|
90
|
+
index: payload.index,
|
|
91
|
+
name: payload.name,
|
|
92
|
+
});
|
|
93
|
+
// Update body uses the exact same wire shape as create — the PUT
|
|
94
|
+
// endpoint is a full replace and was not affected by the bulk migration.
|
|
95
|
+
export const toUpdateCardPolicyTemplateRequestBody = toCreateCardPolicyTemplateRequestBody;
|
|
96
|
+
/**
|
|
97
|
+
* Seed an edit-form draft (`CreateCardPolicyTemplateRequest`) from an
|
|
98
|
+
* existing camelCased `CardPolicyTemplate`. Used by the detail-fetch
|
|
99
|
+
* epic so the edit page can render with the saved values pre-populated.
|
|
100
|
+
*/
|
|
101
|
+
export const toCardPolicyEditFormDraft = (template) => ({
|
|
102
|
+
allowedEntity: {
|
|
103
|
+
categoryCodes: [...template.allowedEntity.categoryCodes],
|
|
104
|
+
merchantNames: [...template.allowedEntity.merchantNames],
|
|
105
|
+
},
|
|
106
|
+
applyToCards: [...template.appliedCards],
|
|
107
|
+
blockedEntity: {
|
|
108
|
+
categoryCodes: [...template.blockedEntity.categoryCodes],
|
|
109
|
+
merchantNames: [...template.blockedEntity.merchantNames],
|
|
110
|
+
},
|
|
111
|
+
description: template.description,
|
|
112
|
+
mode: template.mode,
|
|
113
|
+
name: template.name,
|
|
114
|
+
requiredReceiptThreshold: template.requiredReceiptThreshold,
|
|
115
|
+
spendLimits: { transaction: template.spendLimits.transaction },
|
|
116
|
+
});
|
|
117
|
+
export const toExtractedCardPolicyRules = (payload) => ({
|
|
118
|
+
allowedEntity: {
|
|
119
|
+
categoryCodes: payload.allowed_entity?.category_codes ?? [],
|
|
120
|
+
merchantNames: payload.allowed_entity?.merchant_names ?? [],
|
|
121
|
+
},
|
|
122
|
+
blockedEntity: {
|
|
123
|
+
categoryCodes: payload.blocked_entity?.category_codes ?? [],
|
|
124
|
+
merchantNames: payload.blocked_entity?.merchant_names ?? [],
|
|
125
|
+
},
|
|
126
|
+
confidenceScore: payload.confidence_score,
|
|
127
|
+
policyName: payload.policy_name,
|
|
128
|
+
requiredReceiptThreshold: payload.required_receipt_threshold,
|
|
129
|
+
transactionLimit: payload.transaction_limit,
|
|
130
|
+
});
|