@zeniai/client-epic-state 5.0.81-betaML5 → 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.
Files changed (189) hide show
  1. package/lib/common/aiCfo/aiCfoSuggestedQuestionsPageContext.d.ts +1 -1
  2. package/lib/commonStateTypes/viewAndReport/viewAndReport.d.ts +2 -2
  3. package/lib/commonStateTypes/viewAndReport/viewAndReport.js +0 -2
  4. package/lib/entity/account/accountSelector.d.ts +0 -9
  5. package/lib/entity/account/accountSelector.js +1 -14
  6. package/lib/entity/account/accountState.d.ts +1 -1
  7. package/lib/entity/accountGroup/accountGroupState.d.ts +1 -1
  8. package/lib/entity/aiCfo/aiCfoPayload.d.ts +171 -1
  9. package/lib/entity/aiCfo/aiCfoReducer.d.ts +22 -2
  10. package/lib/entity/aiCfo/aiCfoReducer.js +291 -4
  11. package/lib/entity/aiCfo/aiCfoSelector.d.ts +3 -1
  12. package/lib/entity/aiCfo/aiCfoSelector.js +11 -1
  13. package/lib/entity/aiCfo/aiCfoState.d.ts +186 -4
  14. package/lib/entity/aiCfo/aiCfoState.js +23 -1
  15. package/lib/entity/cardPolicy/cardPolicyPayload.d.ts +277 -0
  16. package/lib/entity/cardPolicy/cardPolicyPayload.js +143 -0
  17. package/lib/entity/cardPolicy/cardPolicyReducer.d.ts +19 -0
  18. package/lib/entity/cardPolicy/cardPolicyReducer.js +256 -0
  19. package/lib/entity/cardPolicy/cardPolicySelector.d.ts +32 -0
  20. package/lib/entity/cardPolicy/cardPolicySelector.js +103 -0
  21. package/lib/entity/cardPolicy/cardPolicyState.d.ts +205 -0
  22. package/lib/entity/cardPolicy/cardPolicyState.js +14 -0
  23. package/lib/entity/cardPolicy/extractPolicyDocumentEpic.d.ts +18 -0
  24. package/lib/entity/cardPolicy/extractPolicyDocumentEpic.js +54 -0
  25. package/lib/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.d.ts +26 -0
  26. package/lib/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.js +47 -0
  27. package/lib/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.d.ts +25 -0
  28. package/lib/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.js +39 -0
  29. package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.d.ts +23 -0
  30. package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.js +92 -0
  31. package/lib/entity/class/classReducer.d.ts +4 -4
  32. package/lib/entity/class/classState.d.ts +1 -1
  33. package/lib/entity/forecast/forecastState.d.ts +1 -1
  34. package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +1 -1
  35. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +1 -1
  36. package/lib/entity/sectionAccountsView/sectionAccountsView.d.ts +1 -1
  37. package/lib/entity/sectionClassesViewV2/sectionClassesView.d.ts +1 -1
  38. package/lib/entity/sectionClassesViewV2/sectionClassesViewReducer.d.ts +2 -2
  39. package/lib/entity/sectionProjectView/sectionProjectView.d.ts +1 -1
  40. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +2 -2
  41. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  42. package/lib/entity/snackbar/snackbarTypes.js +2 -1
  43. package/lib/entity/tenant/clearAllEpic.d.ts +6 -3
  44. package/lib/entity/tenant/clearAllEpic.js +8 -2
  45. package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +0 -4
  46. package/lib/entity/transaction/payloadTypes/transactionPayload.js +10 -18
  47. package/lib/entity/transaction/stateTypes/transaction.d.ts +0 -3
  48. package/lib/epic.d.ts +11 -3
  49. package/lib/epic.js +11 -3
  50. package/lib/esm/commonStateTypes/viewAndReport/viewAndReport.js +0 -2
  51. package/lib/esm/entity/account/accountSelector.js +0 -11
  52. package/lib/esm/entity/aiCfo/aiCfoReducer.js +291 -4
  53. package/lib/esm/entity/aiCfo/aiCfoSelector.js +10 -1
  54. package/lib/esm/entity/aiCfo/aiCfoState.js +20 -0
  55. package/lib/esm/entity/cardPolicy/cardPolicyPayload.js +130 -0
  56. package/lib/esm/entity/cardPolicy/cardPolicyReducer.js +252 -0
  57. package/lib/esm/entity/cardPolicy/cardPolicySelector.js +79 -0
  58. package/lib/esm/entity/cardPolicy/cardPolicyState.js +9 -0
  59. package/lib/esm/entity/cardPolicy/extractPolicyDocumentEpic.js +50 -0
  60. package/lib/esm/entity/cardPolicy/fetchCardPolicyVendorOptionsEpic.js +43 -0
  61. package/lib/esm/entity/cardPolicy/policyDocumentExtractionToRecommendationBridgeEpic.js +35 -0
  62. package/lib/esm/entity/cardPolicy/policyRecommendationFromUploadEpic.js +88 -0
  63. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -1
  64. package/lib/esm/entity/tenant/clearAllEpic.js +8 -2
  65. package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +10 -18
  66. package/lib/esm/epic.js +11 -3
  67. package/lib/esm/index.js +26 -12
  68. package/lib/esm/reducer.js +12 -3
  69. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +6 -3
  70. package/lib/esm/view/aiCfoView/epics/createSessionAndSubmitEpic.js +5 -2
  71. package/lib/esm/view/aiCfoView/epics/createSessionEpic.js +2 -1
  72. package/lib/esm/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
  73. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +1 -13
  74. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -9
  75. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.js +70 -0
  76. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.js +22 -0
  77. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.js +1 -0
  78. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.js +56 -0
  79. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.js +49 -0
  80. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.js +42 -0
  81. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.js +72 -0
  82. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.js +21 -0
  83. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.js +1 -0
  84. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.js +56 -0
  85. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +62 -0
  86. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +39 -0
  87. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.js +1 -0
  88. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.js +53 -0
  89. package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.js +25 -0
  90. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +25 -0
  91. package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.js +2 -2
  92. package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +16 -4
  93. package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +38 -0
  94. package/lib/esm/view/transactionDetail/transactionDetailSelector.js +1 -6
  95. package/lib/index.d.ts +24 -12
  96. package/lib/index.js +147 -43
  97. package/lib/reducer.d.ts +12 -3
  98. package/lib/reducer.js +12 -3
  99. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +11 -2
  100. package/lib/view/aiCfoView/aiCfoViewReducer.js +6 -3
  101. package/lib/view/aiCfoView/epics/createSessionAndSubmitEpic.js +5 -2
  102. package/lib/view/aiCfoView/epics/createSessionEpic.js +2 -1
  103. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.d.ts +8 -1
  104. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
  105. package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
  106. package/lib/view/billPayCard/billPayCardSelector.d.ts +1 -1
  107. package/lib/view/cardBalance/cardBalanceSelector.d.ts +1 -1
  108. package/lib/view/cashBalance/cashBalanceSelector.d.ts +1 -1
  109. package/lib/view/cashInCashOut/cashInCashOutSelector.d.ts +1 -1
  110. package/lib/view/cashPosition/cashPositionSelector.d.ts +1 -1
  111. package/lib/view/dashboard/dashboardReducer.d.ts +2 -2
  112. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  113. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +1 -3
  114. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +2 -14
  115. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +1 -4
  116. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +0 -1
  117. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -11
  118. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +1 -1
  119. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +2 -2
  120. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -1
  121. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +1 -1
  122. package/lib/view/financeStatement/financeStatementReducer.d.ts +1 -1
  123. package/lib/view/globalMerchantView/globalMerchantViewReducer.d.ts +2 -2
  124. package/lib/view/people/peopleTypes.d.ts +1 -1
  125. package/lib/view/reimbursementCard/reimbursementCardSelector.d.ts +1 -1
  126. package/lib/view/reportsResync/reportsResyncReducer.d.ts +2 -2
  127. package/lib/view/scheduleView/scheduleListView/scheduleListTypes.d.ts +1 -1
  128. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.d.ts +14 -0
  129. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailReducer.js +74 -0
  130. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.d.ts +21 -0
  131. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailSelector.js +28 -0
  132. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.d.ts +25 -0
  133. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/cardPolicyDetailState.js +2 -0
  134. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.d.ts +19 -0
  135. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/fetchCardPolicyDetailEpic.js +60 -0
  136. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.d.ts +18 -0
  137. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyDetail/updateCardPolicyEpic.js +53 -0
  138. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.d.ts +16 -0
  139. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/archiveCardPolicyEpic.js +46 -0
  140. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.d.ts +15 -0
  141. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListReducer.js +76 -0
  142. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.d.ts +21 -0
  143. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListSelector.js +27 -0
  144. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.d.ts +19 -0
  145. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/cardPolicyListState.js +2 -0
  146. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.d.ts +19 -0
  147. package/lib/view/spendManagement/chargeCards/cardPolicy/cardPolicyList/fetchCardPolicyListEpic.js +60 -0
  148. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.d.ts +13 -0
  149. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +66 -0
  150. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.d.ts +32 -0
  151. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +46 -0
  152. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.d.ts +45 -0
  153. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.js +2 -0
  154. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.d.ts +25 -0
  155. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyTemplateEpic.js +57 -0
  156. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.d.ts +15 -0
  157. package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/fetchCardPolicyMccCategoriesEpic.js +29 -0
  158. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.d.ts +1 -1
  159. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.d.ts +14 -0
  160. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +27 -1
  161. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.d.ts +2 -2
  162. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.js +1 -1
  163. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.d.ts +5 -1
  164. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +17 -5
  165. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.d.ts +27 -0
  166. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +43 -1
  167. package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardState.d.ts +2 -0
  168. package/lib/view/taskManager/taskListView/taskListReducer.d.ts +1 -1
  169. package/lib/view/tasksCard/tasksCardSelector.d.ts +1 -1
  170. package/lib/view/topEx/topExSelector.d.ts +1 -1
  171. package/lib/view/transactionDetail/transactionDetailPayload.d.ts +1 -1
  172. package/lib/view/transactionDetail/transactionDetailSelector.d.ts +1 -6
  173. package/lib/view/transactionDetail/transactionDetailSelector.js +1 -6
  174. package/package.json +1 -1
  175. package/lib/esm/view/createTransferEntry/createTransferEntryReducer.js +0 -82
  176. package/lib/esm/view/createTransferEntry/createTransferEntrySelector.js +0 -64
  177. package/lib/esm/view/createTransferEntry/createTransferEntryState.js +0 -17
  178. package/lib/esm/view/createTransferEntry/epics/createTransferEntryEpic.js +0 -130
  179. package/lib/esm/view/createTransferEntry/epics/fetchTransferAccountsEpic.js +0 -37
  180. package/lib/view/createTransferEntry/createTransferEntryReducer.d.ts +0 -32
  181. package/lib/view/createTransferEntry/createTransferEntryReducer.js +0 -86
  182. package/lib/view/createTransferEntry/createTransferEntrySelector.d.ts +0 -39
  183. package/lib/view/createTransferEntry/createTransferEntrySelector.js +0 -69
  184. package/lib/view/createTransferEntry/createTransferEntryState.d.ts +0 -30
  185. package/lib/view/createTransferEntry/createTransferEntryState.js +0 -20
  186. package/lib/view/createTransferEntry/epics/createTransferEntryEpic.d.ts +0 -11
  187. package/lib/view/createTransferEntry/epics/createTransferEntryEpic.js +0 -134
  188. package/lib/view/createTransferEntry/epics/fetchTransferAccountsEpic.d.ts +0 -14
  189. 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: toAiCfoVisualizationTypeStrict(visualization.type) ?? 'table',
140
- data: toAiCfoVisualizationData(visualization.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 { allSessionsWithOrderedQuestionAnswers };
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
+ });