@zeniai/client-epic-state 5.0.82-betaAS1 → 5.0.83-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/entity/aiCfo/aiCfoPayload.d.ts +140 -1
- package/lib/entity/aiCfo/aiCfoReducer.d.ts +23 -2
- package/lib/entity/aiCfo/aiCfoReducer.js +328 -5
- package/lib/entity/aiCfo/aiCfoSelector.d.ts +13 -1
- package/lib/entity/aiCfo/aiCfoSelector.js +28 -1
- package/lib/entity/aiCfo/aiCfoState.d.ts +225 -4
- package/lib/entity/aiCfo/aiCfoState.js +30 -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 +175 -0
- package/lib/entity/cardPolicy/cardPolicySelector.d.ts +32 -0
- package/lib/entity/cardPolicy/cardPolicySelector.js +99 -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 +68 -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 +104 -0
- package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +1 -1
- package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +3 -0
- package/lib/entity/tenant/clearAllEpic.d.ts +6 -2
- package/lib/entity/tenant/clearAllEpic.js +8 -0
- package/lib/entity/tenant/tenantPayload.d.ts +0 -3
- package/lib/entity/tenant/tenantReducer.d.ts +1 -1
- package/lib/entity/tenant/tenantReducer.js +3 -23
- package/lib/entity/tenant/tenantState.d.ts +0 -3
- package/lib/epic.d.ts +11 -3
- package/lib/epic.js +11 -3
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +328 -5
- package/lib/esm/entity/aiCfo/aiCfoSelector.js +26 -1
- package/lib/esm/entity/aiCfo/aiCfoState.js +27 -0
- package/lib/esm/entity/cardPolicy/cardPolicyPayload.js +130 -0
- package/lib/esm/entity/cardPolicy/cardPolicyReducer.js +171 -0
- package/lib/esm/entity/cardPolicy/cardPolicySelector.js +75 -0
- package/lib/esm/entity/cardPolicy/cardPolicyState.js +9 -0
- package/lib/esm/entity/cardPolicy/extractPolicyDocumentEpic.js +64 -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 +100 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +3 -0
- package/lib/esm/entity/tenant/clearAllEpic.js +8 -0
- package/lib/esm/entity/tenant/tenantReducer.js +2 -22
- package/lib/esm/epic.js +11 -3
- package/lib/esm/index.js +29 -11
- package/lib/esm/reducer.js +12 -0
- 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/dashboard/dashboardReducer.js +5 -1
- package/lib/esm/view/dashboard/dashboardSelector.js +2 -1
- 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 +45 -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/aiCardCreationFormDraftTypes.js +1 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +62 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +47 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardEpic.js +2 -2
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +62 -4
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +61 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +69 -0
- package/lib/esm/view/spendManagement/treasury/treasurySetUp/treasurySetupViewReducer.js +1 -45
- package/lib/index.d.ts +28 -11
- package/lib/index.js +154 -29
- package/lib/reducer.d.ts +12 -0
- package/lib/reducer.js +12 -0
- 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/dashboard/dashboardReducer.d.ts +1 -1
- package/lib/view/dashboard/dashboardReducer.js +6 -2
- package/lib/view/dashboard/dashboardSelector.d.ts +1 -0
- package/lib/view/dashboard/dashboardSelector.js +2 -1
- package/lib/view/dashboard/dashboardState.d.ts +1 -0
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -1
- package/lib/view/people/peopleTypes.d.ts +1 -1
- 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 +49 -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/aiCardCreationFormDraftTypes.d.ts +79 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.js +2 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.d.ts +25 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +66 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.d.ts +26 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +51 -0
- 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 +12 -2
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +63 -5
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.d.ts +42 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +68 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardState.d.ts +18 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.d.ts +11 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +73 -0
- package/lib/view/spendManagement/treasury/treasurySetUp/treasurySetupViewReducer.d.ts +1 -1
- package/lib/view/spendManagement/treasury/treasurySetUp/treasurySetupViewReducer.js +2 -46
- package/lib/view/spendManagement/treasury/treasurySetUp/treasurySetupViewState.d.ts +0 -2
- package/lib/view/taskManager/taskListView/taskListReducer.d.ts +1 -1
- package/package.json +1 -1
- package/lib/esm/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.js +0 -18
- package/lib/esm/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.js +0 -18
- package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.d.ts +0 -8
- package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.js +0 -22
- package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.d.ts +0 -8
- package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.js +0 -22
|
@@ -16,4 +16,4 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @see README.md in this directory for maintenance expectations.
|
|
18
18
|
*/
|
|
19
|
-
export type AiCfoSuggestedQuestionsPageContext = 'balance_sheet' | 'profit_loss' | 'vendors' | 'transactions' | 'cash_flow' | 'accounts_receivable' | 'accounts_payable' | 'transaction_details' | 'dashboard' | 'cash_balance' | 'card_balance' | 'cash_in_cash_out' | 'net_burn_or_income' | 'cash_position' | 'top_expenses' | 'revenue' | 'operating_expenses' | 'command_centre' | 'je_schedules' | 'reconciliation' | 'flux_analysis' | 'receipts' | 'mei' | 'task_checklist' | 'performance' | 'bills_list' | 'bills_details' | 'reimbursement_list' | 'reimbursement_details' | 'vendors_summary' | 'opex_by_vendor' | 'prepaid_expenses' | 'fixed_assets' | 'form_1099' | 'accrued_expenses' | 'forecast' | 'checking' | 'checking_details' | 'treasury' | 'cards' | 'cards_detail' | 'people' | 'rewards' | 'notifications';
|
|
19
|
+
export type AiCfoSuggestedQuestionsPageContext = 'balance_sheet' | 'profit_loss' | 'vendors' | 'transactions' | 'cash_flow' | 'accounts_receivable' | 'accounts_payable' | 'transaction_details' | 'dashboard' | 'cash_balance' | 'card_balance' | 'cash_in_cash_out' | 'net_burn_or_income' | 'cash_position' | 'top_expenses' | 'revenue' | 'operating_expenses' | 'command_centre' | 'je_schedules' | 'reconciliation' | 'flux_analysis' | 'receipts' | 'mei' | 'task_checklist' | 'performance' | 'bills_list' | 'bills_details' | 'reimbursement_list' | 'reimbursement_details' | 'vendors_summary' | 'opex_by_vendor' | 'prepaid_expenses' | 'fixed_assets' | 'form_1099' | 'accrued_expenses' | 'forecast' | 'checking' | 'checking_details' | 'treasury' | 'cards' | 'cards_creation' | 'cards_detail' | 'card_policy' | 'card_policy_detail' | 'people' | 'rewards' | 'notifications';
|
|
@@ -20,8 +20,147 @@ export interface ChartVisualizationPayload {
|
|
|
20
20
|
lines?: string[] | null;
|
|
21
21
|
y_format?: YFormatPayload;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Single AI-suggested card on the wire. The controller ships an array of
|
|
25
|
+
* these mixing vendor, department, and category entries. Entries with
|
|
26
|
+
* `is_suggested: true` seed step 1 (`review`) or the step-2 editable table
|
|
27
|
+
* (`create`); entries with `is_suggested: false` aren't pre-rendered as
|
|
28
|
+
* rows but are available in the step-2 Card dropdown (and, in `review`,
|
|
29
|
+
* the step-1 "Add something else" search).
|
|
30
|
+
*
|
|
31
|
+
* `owner_id` may be `null` (or empty string, defensively normalized by the
|
|
32
|
+
* reducer) when the AI hasn't picked an owner.
|
|
33
|
+
*/
|
|
34
|
+
export interface SuggestedCardPayload {
|
|
35
|
+
avg_spend: number;
|
|
36
|
+
card_name: string;
|
|
37
|
+
card_name_id: string;
|
|
38
|
+
card_type: 'vendor' | 'department' | 'category';
|
|
39
|
+
credit_limit: number;
|
|
40
|
+
is_suggested: boolean;
|
|
41
|
+
owner_id: string | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wire payload for the AI Card Creation interactive form. Both `review`
|
|
45
|
+
* and `create` modes share an identical envelope — a flat
|
|
46
|
+
* `suggested_cards[]` (mix of suggested + not-suggested rows). The only
|
|
47
|
+
* thing that varies is `mode`, which the FE uses to pick the step UX:
|
|
48
|
+
*
|
|
49
|
+
* - `review` (2-step): user reviews / toggles the `is_suggested: true`
|
|
50
|
+
* rows in step 1, then configures one card per surviving row in step 2.
|
|
51
|
+
* - `create` (1-step): the FE seeds the step-2 editable table directly
|
|
52
|
+
* from the `is_suggested: true` rows; step 1 is skipped.
|
|
53
|
+
*
|
|
54
|
+
* In both modes the full `suggested_cards[]` universe (including
|
|
55
|
+
* `is_suggested: false`) feeds the step-2 Card-cell dropdown so the user
|
|
56
|
+
* can swap a row to any vendor / department / category of the same kind.
|
|
57
|
+
*
|
|
58
|
+
* Note: `card_options` (physical / virtual / subscription) is NOT part of
|
|
59
|
+
* the wire. The FE injects a single `virtual` option via
|
|
60
|
+
* `AI_CARD_CREATION_VIRTUAL_ONLY_CARD_OPTIONS`; the reducer defaults each
|
|
61
|
+
* seeded row's `cardOptionId` to `'virtual'`.
|
|
62
|
+
*/
|
|
63
|
+
export interface CardsCreationInitialDataPayload {
|
|
64
|
+
mode: 'review' | 'create';
|
|
65
|
+
suggested_cards: SuggestedCardPayload[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Per-policy allow / block bucket on the wire. The two arrays are
|
|
69
|
+
* independent (a draft policy may populate both, only one, or neither).
|
|
70
|
+
*/
|
|
71
|
+
export interface DraftPolicyEntityPayload {
|
|
72
|
+
category_codes: string[];
|
|
73
|
+
merchant_names: string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Spend-limit bucket per policy. `transaction` is the only cadence the
|
|
77
|
+
* BE ships today; left open-ended for future cadences (e.g.
|
|
78
|
+
* `monthly`, `quarterly`).
|
|
79
|
+
*/
|
|
80
|
+
export interface DraftPolicySpendLimitsPayload {
|
|
81
|
+
transaction: number;
|
|
82
|
+
[cadence: string]: number | undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Single draft policy on the wire. The controller emits one of these
|
|
86
|
+
* per recommended policy in `step_5_review.draft_policies`. Identity is
|
|
87
|
+
* positional (no stable `id` from the BE); the FE synthesizes one on
|
|
88
|
+
* decode.
|
|
89
|
+
*/
|
|
90
|
+
export interface DraftPolicyPayload {
|
|
91
|
+
allowed_entity: DraftPolicyEntityPayload;
|
|
92
|
+
apply_to_cards: string[];
|
|
93
|
+
blocked_entity: DraftPolicyEntityPayload;
|
|
94
|
+
description: string;
|
|
95
|
+
/** Enforcement mode for the policy (e.g. `"strict"`). Carried into
|
|
96
|
+
* state for guided submit; ignored for `auto` / `upload`. */
|
|
97
|
+
mode: string;
|
|
98
|
+
name: string;
|
|
99
|
+
required_receipt_threshold: number;
|
|
100
|
+
spend_limits: DraftPolicySpendLimitsPayload;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* `step_5_review` block — the only step in `wizard_plan` the FE reads.
|
|
104
|
+
* Steps 1–4 are intentionally not modeled.
|
|
105
|
+
*/
|
|
106
|
+
export interface CardPolicyStep5ReviewPayload {
|
|
107
|
+
draft_policies: DraftPolicyPayload[];
|
|
108
|
+
/** Header text shown above the review list / wizard. */
|
|
109
|
+
prompt: string;
|
|
110
|
+
/** How the BE grouped the suggested policies. `"none"` today; may
|
|
111
|
+
* surface as a header banner in a future iteration. */
|
|
112
|
+
grouping_source?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface CardPolicyWizardPlanPayload {
|
|
115
|
+
step_5_review: CardPolicyStep5ReviewPayload;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Upload-mode metadata that the `policy-recommendation-from-upload` API
|
|
119
|
+
* returns alongside the wizard plan. Absent on the initial controller
|
|
120
|
+
* response; populated after the file upload + recommendation chain.
|
|
121
|
+
*/
|
|
122
|
+
export interface CardPolicyUploadSourcePayload {
|
|
123
|
+
confidence_score: number;
|
|
124
|
+
file_name: string;
|
|
125
|
+
low_confidence_fields: string[];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Discriminated payload for the AI Card Policy interactive form.
|
|
129
|
+
* All three modes share the same `wizard_plan.step_5_review.draft_policies`
|
|
130
|
+
* envelope; the FE renders different forms based on `mode`.
|
|
131
|
+
*
|
|
132
|
+
* - `auto` (1-step review): user reviews suggested policies and submits.
|
|
133
|
+
* - `guided` (5-step wizard): Steps 1–4 seeded from `draft_policies[0]`,
|
|
134
|
+
* Step 5 lists every draft policy.
|
|
135
|
+
* - `upload` (3-step): upload doc → pick cards → review. Controller
|
|
136
|
+
* responses for `mode === "upload"` arrive *before* the user uploads,
|
|
137
|
+
* so `wizard_plan` is optional; populated after the upload-recommendation
|
|
138
|
+
* API returns.
|
|
139
|
+
*/
|
|
140
|
+
export type CardPolicyInitialDataPayload = {
|
|
141
|
+
mode: 'auto';
|
|
142
|
+
wizard_plan: CardPolicyWizardPlanPayload;
|
|
143
|
+
} | {
|
|
144
|
+
mode: 'guided';
|
|
145
|
+
wizard_plan: CardPolicyWizardPlanPayload;
|
|
146
|
+
} | {
|
|
147
|
+
mode: 'upload';
|
|
148
|
+
source?: CardPolicyUploadSourcePayload;
|
|
149
|
+
wizard_plan?: CardPolicyWizardPlanPayload;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Discriminated visualization payload. The FE narrows on `form_type` and
|
|
153
|
+
* casts `payload` to the matching shape.
|
|
154
|
+
*/
|
|
155
|
+
export type InteractiveFormVisualizationPayload = {
|
|
156
|
+
form_type: 'cards_creation';
|
|
157
|
+
payload: CardsCreationInitialDataPayload;
|
|
158
|
+
} | {
|
|
159
|
+
form_type: 'card_policy';
|
|
160
|
+
payload: CardPolicyInitialDataPayload;
|
|
161
|
+
};
|
|
23
162
|
export interface AiCfoVisualizationPayload {
|
|
24
|
-
data: TableVisualizationPayload | ChartVisualizationPayload | null;
|
|
163
|
+
data: TableVisualizationPayload | ChartVisualizationPayload | InteractiveFormVisualizationPayload | null;
|
|
25
164
|
type: string | null;
|
|
26
165
|
}
|
|
27
166
|
export interface AiCfoAnswerPayload {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ID } from '../../commonStateTypes/common';
|
|
2
2
|
import { ZeniDate } from '../../zeniDayJS';
|
|
3
3
|
import { AiCfoAnswerPayload, AiCfoVisualizationPayload, ChatSessionPayload, MessagePayload } from './aiCfoPayload';
|
|
4
|
-
import { AiCfoState, AiCfoVisualization, FileAttachmentMetadata } from './aiCfoState';
|
|
4
|
+
import { AiCfoState, AiCfoVisualization, FileAttachmentMetadata, SyntheticAiCfoAnswerKind } from './aiCfoState';
|
|
5
|
+
import type { CardPolicyUploadSource, CardPolicyWizardPlan } from './aiCfoState';
|
|
5
6
|
export declare const initialAiCfoState: AiCfoState;
|
|
6
7
|
export declare const toAiCfoVisualization: (visualization: AiCfoVisualizationPayload) => AiCfoVisualization | undefined;
|
|
7
8
|
export declare const setNewSession: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
@@ -41,6 +42,26 @@ export declare const setNewSession: import("@reduxjs/toolkit").ActionCreatorWith
|
|
|
41
42
|
}, "aiCfo/addQuestionPayload">, clearAiCfo: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"aiCfo/clearAiCfo">, deleteQuestion: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
42
43
|
chatSessionId: ID;
|
|
43
44
|
questionAnswerId: ID;
|
|
44
|
-
}, "aiCfo/deleteQuestion"
|
|
45
|
+
}, "aiCfo/deleteQuestion">, updateAiCfoAnswerCardPolicyWizardPlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
46
|
+
answerId: ID;
|
|
47
|
+
chatSessionId: ID;
|
|
48
|
+
wizardPlan: CardPolicyWizardPlan;
|
|
49
|
+
source?: CardPolicyUploadSource;
|
|
50
|
+
}, "aiCfo/updateAiCfoAnswerCardPolicyWizardPlan">, appendSyntheticAiCfoAnswer: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
51
|
+
chatSessionId: ID;
|
|
52
|
+
createdAt: ZeniDate;
|
|
53
|
+
id: ID;
|
|
54
|
+
kind: SyntheticAiCfoAnswerKind;
|
|
55
|
+
sourceQuestionAnswerId: ID;
|
|
56
|
+
entityIds?: ID[];
|
|
57
|
+
}, "aiCfo/appendSyntheticAiCfoAnswer">, updateSyntheticAiCfoAnswer: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
58
|
+
chatSessionId: ID;
|
|
59
|
+
id: ID;
|
|
60
|
+
kind: SyntheticAiCfoAnswerKind;
|
|
61
|
+
entityIds?: ID[];
|
|
62
|
+
}, "aiCfo/updateSyntheticAiCfoAnswer">, removeSyntheticAiCfoAnswer: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
63
|
+
chatSessionId: ID;
|
|
64
|
+
id: ID;
|
|
65
|
+
}, "aiCfo/removeSyntheticAiCfoAnswer">, clearSyntheticAiCfoAnswers: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "aiCfo/clearSyntheticAiCfoAnswers">;
|
|
45
66
|
declare const _default: import("redux").Reducer<AiCfoState>;
|
|
46
67
|
export default _default;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.deleteQuestion = exports.clearAiCfo = exports.addQuestionPayload = exports.upsertOrAddQuestionAnswerPayload = exports.upsertAnswerPayload = exports.setChatHistory = exports.setSessions = exports.clearSession = exports.setNewSession = exports.toAiCfoVisualization = exports.initialAiCfoState = void 0;
|
|
4
|
+
exports.clearSyntheticAiCfoAnswers = exports.removeSyntheticAiCfoAnswer = exports.updateSyntheticAiCfoAnswer = exports.appendSyntheticAiCfoAnswer = exports.updateAiCfoAnswerCardPolicyWizardPlan = exports.deleteQuestion = exports.clearAiCfo = exports.addQuestionPayload = exports.upsertOrAddQuestionAnswerPayload = exports.upsertAnswerPayload = exports.setChatHistory = exports.setSessions = exports.clearSession = exports.setNewSession = exports.toAiCfoVisualization = exports.initialAiCfoState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const zeniDayJS_1 = require("../../zeniDayJS");
|
|
7
7
|
const aiCfoState_1 = require("./aiCfoState");
|
|
8
8
|
exports.initialAiCfoState = {
|
|
9
9
|
aiCfoByChatSessionId: {},
|
|
10
10
|
partialQuestionAnswers: {},
|
|
11
|
+
syntheticAnswersByChatSessionId: {},
|
|
11
12
|
};
|
|
12
13
|
// Utility: Merge two arrays of Q&A pairs, combining responses for the same question
|
|
13
14
|
function mergeQuestionAnswerPairs(existing, newQAs) {
|
|
@@ -139,12 +140,219 @@ const toAiCfoVisualization = (visualization) => {
|
|
|
139
140
|
if (visualization.type == null || visualization.data == null) {
|
|
140
141
|
return undefined;
|
|
141
142
|
}
|
|
143
|
+
// Reject unknown viz types instead of silently defaulting to `table`.
|
|
144
|
+
// The previous `?? 'table'` fallback funneled any non-strict-union
|
|
145
|
+
// payload into `toAiCfoVisualizationData`, which is the table/chart
|
|
146
|
+
// decoder and dereferences `data.values.map(...)`. Interactive-form
|
|
147
|
+
// payloads (e.g. when the BE ships `type: <new_form_kind>` we don't
|
|
148
|
+
// know about yet) carry no `values`, so that path threw during answer
|
|
149
|
+
// upsert and broke rendering for the whole Q/A. Returning `undefined`
|
|
150
|
+
// matches the existing "missing type / data" guard above — callers
|
|
151
|
+
// already handle a no-visualization answer.
|
|
152
|
+
const normalizedType = (0, aiCfoState_1.toAiCfoVisualizationTypeStrict)(visualization.type);
|
|
153
|
+
if (normalizedType == null) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
142
156
|
return {
|
|
143
|
-
type:
|
|
144
|
-
data:
|
|
157
|
+
type: normalizedType,
|
|
158
|
+
data: normalizedType === 'interactive_form'
|
|
159
|
+
? toInteractiveFormVisualization(visualization.data)
|
|
160
|
+
: toAiCfoVisualizationData(visualization.data),
|
|
145
161
|
};
|
|
146
162
|
};
|
|
147
163
|
exports.toAiCfoVisualization = toAiCfoVisualization;
|
|
164
|
+
const toInteractiveFormVisualization = (data) => {
|
|
165
|
+
// When `form_type` is missing / mistyped, fall back to a payload-shape
|
|
166
|
+
// heuristic instead of blindly defaulting to `cards_creation`. A
|
|
167
|
+
// `card_policy` payload always carries `mode: 'auto' | 'guided' |
|
|
168
|
+
// 'upload'` (see `CardPolicyInitialDataPayload`); a `cards_creation`
|
|
169
|
+
// payload carries `suggested_cards` and never that exact `mode` set.
|
|
170
|
+
// Without this, a card-policy answer with a malformed `form_type`
|
|
171
|
+
// would be decoded as cards_creation, surfacing the wrong interactive
|
|
172
|
+
// form (and wrong submit path) in the UI.
|
|
173
|
+
const formType = (0, aiCfoState_1.toInteractiveFormTypeStrict)(data.form_type) ??
|
|
174
|
+
inferInteractiveFormTypeFromPayload(data.payload);
|
|
175
|
+
if (formType === 'card_policy') {
|
|
176
|
+
return {
|
|
177
|
+
formType: 'card_policy',
|
|
178
|
+
payload: toCardPolicyInitialData(data.payload),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
formType: 'cards_creation',
|
|
183
|
+
payload: toCardsCreationInitialData(data.payload),
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
const inferInteractiveFormTypeFromPayload = (payload) => {
|
|
187
|
+
if (payload != null && typeof payload === 'object' && 'mode' in payload) {
|
|
188
|
+
const mode = payload.mode;
|
|
189
|
+
if (mode === 'auto' || mode === 'guided' || mode === 'upload') {
|
|
190
|
+
return 'card_policy';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return 'cards_creation';
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Decode the unified `cards_creation` interactive-form payload into the FE
|
|
197
|
+
* state shape. Both `review` and `create` modes ship the same envelope
|
|
198
|
+
* (`{mode, suggested_cards[]}`); the only thing that varies is how the
|
|
199
|
+
* common derivations are exposed:
|
|
200
|
+
*
|
|
201
|
+
* - `review` (2-step): `is_suggested: true` rows -> `teamRows`; the full
|
|
202
|
+
* universe -> `cardNameOptions` (step-2 swap + step-1 "Add something
|
|
203
|
+
* else" search).
|
|
204
|
+
* - `create` (1-step): `is_suggested: true` rows -> `cardRows` pre-seeded
|
|
205
|
+
* for the step-2 editable table (with `cardOptionId` defaulted to
|
|
206
|
+
* `'virtual'`); the full universe -> `cardNameOptions` (step-2 swap).
|
|
207
|
+
*
|
|
208
|
+
* `cardOptions` (physical / virtual / subscription) is intentionally not
|
|
209
|
+
* derived here — the wire never carries it. The component prop is fed
|
|
210
|
+
* from `AI_CARD_CREATION_VIRTUAL_ONLY_CARD_OPTIONS` at render time. The
|
|
211
|
+
* state field is preserved as `[]` for backward compatibility with
|
|
212
|
+
* consumers that still narrow on it.
|
|
213
|
+
*/
|
|
214
|
+
const toCardsCreationInitialData = (payload) => {
|
|
215
|
+
const suggestedCards = payload.suggested_cards ?? [];
|
|
216
|
+
const ordered = [...suggestedCards].sort((a, b) => KIND_RANK[a.card_type] - KIND_RANK[b.card_type]);
|
|
217
|
+
const withUniqueIds = withSynthesizedIds(ordered);
|
|
218
|
+
const suggestedOnly = withUniqueIds.filter(({ card }) => card.is_suggested);
|
|
219
|
+
const cardNameOptions = withUniqueIds.map(({ card, uniqueId }) => ({
|
|
220
|
+
id: uniqueId,
|
|
221
|
+
kind: toCardKind(card.card_type),
|
|
222
|
+
label: card.card_name,
|
|
223
|
+
}));
|
|
224
|
+
const suggestedOwnerUserIdByTeamId = Object.fromEntries(suggestedOnly.map(({ card, uniqueId }) => [
|
|
225
|
+
uniqueId,
|
|
226
|
+
normalizeOwnerId(card.owner_id),
|
|
227
|
+
]));
|
|
228
|
+
const common = {
|
|
229
|
+
cardNameOptions,
|
|
230
|
+
cardOptions: [],
|
|
231
|
+
suggestedOwnerUserIdByTeamId,
|
|
232
|
+
};
|
|
233
|
+
if (payload.mode === 'create') {
|
|
234
|
+
return {
|
|
235
|
+
...common,
|
|
236
|
+
mode: 'create',
|
|
237
|
+
cardRows: suggestedOnly.map(({ card, uniqueId }) => ({
|
|
238
|
+
cardKind: toCardKind(card.card_type),
|
|
239
|
+
cardOptionId: aiCfoState_1.CARDS_CREATION_DEFAULT_CARD_OPTION_ID,
|
|
240
|
+
id: uniqueId,
|
|
241
|
+
label: card.card_name,
|
|
242
|
+
recommendedLimit: card.credit_limit > 0 ? card.credit_limit : undefined,
|
|
243
|
+
suggestedOwnerUserId: normalizeOwnerId(card.owner_id),
|
|
244
|
+
})),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
...common,
|
|
249
|
+
mode: 'review',
|
|
250
|
+
teamRows: suggestedOnly.map(({ card, uniqueId }) => ({
|
|
251
|
+
avgMonthlySpend: CURRENCY_FORMATTER.format(card.avg_spend),
|
|
252
|
+
cardKind: toCardKind(card.card_type),
|
|
253
|
+
id: uniqueId,
|
|
254
|
+
label: card.card_name,
|
|
255
|
+
recommendedLimit: CURRENCY_FORMATTER.format(card.credit_limit),
|
|
256
|
+
recommendedLimitCadence: 'Monthly',
|
|
257
|
+
})),
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', {
|
|
261
|
+
style: 'currency',
|
|
262
|
+
currency: 'USD',
|
|
263
|
+
maximumFractionDigits: 0,
|
|
264
|
+
});
|
|
265
|
+
/**
|
|
266
|
+
* Normalize the controller's `owner_id` into `string | undefined`. The
|
|
267
|
+
* BE may emit either `null` or `""` to signal "no owner picked"; both
|
|
268
|
+
* collapse to `undefined` so downstream resolvers don't try to look up
|
|
269
|
+
* a user with an empty id.
|
|
270
|
+
*/
|
|
271
|
+
const normalizeOwnerId = (ownerId) => ownerId != null && ownerId !== '' ? ownerId : undefined;
|
|
272
|
+
const KIND_RANK = {
|
|
273
|
+
vendor: 0,
|
|
274
|
+
category: 1,
|
|
275
|
+
department: 2,
|
|
276
|
+
};
|
|
277
|
+
const toCardKind = (cardType) => {
|
|
278
|
+
if (cardType === 'department') {
|
|
279
|
+
return 'class';
|
|
280
|
+
}
|
|
281
|
+
if (cardType === 'category') {
|
|
282
|
+
return 'category';
|
|
283
|
+
}
|
|
284
|
+
return 'vendor';
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Stamp each suggested card with a stable unique id derived from its
|
|
288
|
+
* `card_name_id`. If two cards in the same payload share the same
|
|
289
|
+
* `card_name_id` (which happens in the current sample payload where
|
|
290
|
+
* `card_name_id` is the literal `"vendor_uuid"`), fall back to a
|
|
291
|
+
* synthesized id `${kind}__${card_name_id}__${index}` so RHF row keying
|
|
292
|
+
* stays unique.
|
|
293
|
+
*/
|
|
294
|
+
const withSynthesizedIds = (cards) => {
|
|
295
|
+
const seen = new Set();
|
|
296
|
+
return cards.map((card, index) => {
|
|
297
|
+
const preferred = card.card_name_id;
|
|
298
|
+
if (preferred != null && preferred !== '' && !seen.has(preferred)) {
|
|
299
|
+
seen.add(preferred);
|
|
300
|
+
return { card, uniqueId: preferred };
|
|
301
|
+
}
|
|
302
|
+
const synthesized = `${card.card_type}__${preferred ?? 'unknown'}__${index}`;
|
|
303
|
+
seen.add(synthesized);
|
|
304
|
+
return { card, uniqueId: synthesized };
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
const toCardPolicyDraftPolicy = (payload, index) => ({
|
|
308
|
+
id: `draft-${index}`,
|
|
309
|
+
name: payload.name,
|
|
310
|
+
description: payload.description ?? '',
|
|
311
|
+
mode: payload.mode,
|
|
312
|
+
allowedEntity: {
|
|
313
|
+
categoryCodes: payload.allowed_entity?.category_codes ?? [],
|
|
314
|
+
merchantNames: payload.allowed_entity?.merchant_names ?? [],
|
|
315
|
+
},
|
|
316
|
+
blockedEntity: {
|
|
317
|
+
categoryCodes: payload.blocked_entity?.category_codes ?? [],
|
|
318
|
+
merchantNames: payload.blocked_entity?.merchant_names ?? [],
|
|
319
|
+
},
|
|
320
|
+
applyToCards: payload.apply_to_cards ?? [],
|
|
321
|
+
requiredReceiptThreshold: payload.required_receipt_threshold ?? 0,
|
|
322
|
+
spendLimits: {
|
|
323
|
+
transaction: payload.spend_limits?.transaction ?? 0,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
const toCardPolicyStep5Review = (payload) => ({
|
|
327
|
+
draftPolicies: (payload.draft_policies ?? []).map(toCardPolicyDraftPolicy),
|
|
328
|
+
groupingSource: payload.grouping_source ?? 'none',
|
|
329
|
+
prompt: payload.prompt ?? '',
|
|
330
|
+
});
|
|
331
|
+
const toCardPolicyWizardPlan = (payload) => ({
|
|
332
|
+
step5Review: toCardPolicyStep5Review(payload.step_5_review),
|
|
333
|
+
});
|
|
334
|
+
const toCardPolicyUploadSource = (payload) => ({
|
|
335
|
+
confidenceScore: payload.confidence_score,
|
|
336
|
+
fileName: payload.file_name,
|
|
337
|
+
lowConfidenceFields: payload.low_confidence_fields ?? [],
|
|
338
|
+
});
|
|
339
|
+
const toCardPolicyInitialData = (payload) => {
|
|
340
|
+
if (payload.mode === 'upload') {
|
|
341
|
+
return {
|
|
342
|
+
policyKind: 'upload',
|
|
343
|
+
wizardPlan: payload.wizard_plan != null
|
|
344
|
+
? toCardPolicyWizardPlan(payload.wizard_plan)
|
|
345
|
+
: undefined,
|
|
346
|
+
source: payload.source != null
|
|
347
|
+
? toCardPolicyUploadSource(payload.source)
|
|
348
|
+
: undefined,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
policyKind: payload.mode,
|
|
353
|
+
wizardPlan: toCardPolicyWizardPlan(payload.wizard_plan),
|
|
354
|
+
};
|
|
355
|
+
};
|
|
148
356
|
const toAiCfoVisualizationData = (data) => {
|
|
149
357
|
if ('x' in data && 'y' in data) {
|
|
150
358
|
// Normalize y to always be an array
|
|
@@ -458,10 +666,118 @@ const aiCfo = (0, toolkit_1.createSlice)({
|
|
|
458
666
|
session.questionAnswers.push(qa);
|
|
459
667
|
}
|
|
460
668
|
},
|
|
669
|
+
/**
|
|
670
|
+
* Patch the `card_policy` interactive-form payload for a specific
|
|
671
|
+
* answer in a chat session to merge in the wizard plan + upload
|
|
672
|
+
* source metadata returned by
|
|
673
|
+
* `POST /cards/1.0/ai-cfo/policy-recommendation-from-upload`.
|
|
674
|
+
*
|
|
675
|
+
* Dispatched by the recommendation epic after a successful response;
|
|
676
|
+
* the wizard re-renders Steps 2 + 3 of the upload variant once the
|
|
677
|
+
* payload becomes a populated `CardPolicyInitialData` with
|
|
678
|
+
* `policyKind: "upload"` + `wizardPlan` + `source`.
|
|
679
|
+
*
|
|
680
|
+
* No-op when the target answer / chat session / form payload is
|
|
681
|
+
* missing or isn't an upload-mode card-policy interactive form —
|
|
682
|
+
* the bridge epic is best-effort and silently drops in edge cases
|
|
683
|
+
* (e.g. the user closed the chat before recommendation returned).
|
|
684
|
+
*/
|
|
685
|
+
updateAiCfoAnswerCardPolicyWizardPlan(draft, action) {
|
|
686
|
+
const { answerId, chatSessionId, source, wizardPlan } = action.payload;
|
|
687
|
+
const session = draft.aiCfoByChatSessionId[chatSessionId];
|
|
688
|
+
if (session == null) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const qa = session.questionAnswers.find((entry) => entry.questionAnswerId === answerId);
|
|
692
|
+
if (qa == null) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
for (const response of Object.values(qa.responses)) {
|
|
696
|
+
const visualization = response?.content.visualization;
|
|
697
|
+
if (visualization == null ||
|
|
698
|
+
visualization.type !== 'interactive_form') {
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
const data = visualization.data;
|
|
702
|
+
if (data.formType !== 'card_policy' ||
|
|
703
|
+
data.payload.policyKind !== 'upload') {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
data.payload = {
|
|
707
|
+
...data.payload,
|
|
708
|
+
wizardPlan,
|
|
709
|
+
source: source ?? data.payload.source,
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
/**
|
|
714
|
+
* Append a client-synthesized answer bubble to a chat session. Used by
|
|
715
|
+
* the AI CFO cards/policy create flow to render a "Creating…" placeholder
|
|
716
|
+
* in the drawer while the underlying mutation runs.
|
|
717
|
+
*
|
|
718
|
+
* The caller is responsible for generating a stable `id` (so the same
|
|
719
|
+
* bubble can later be updated in place via `updateSyntheticAiCfoAnswer`
|
|
720
|
+
* once the mutation completes) and a `createdAt` timestamp so the
|
|
721
|
+
* renderer can interleave synthetic bubbles with streamed Q&A pairs in
|
|
722
|
+
* chronological order.
|
|
723
|
+
*/
|
|
724
|
+
appendSyntheticAiCfoAnswer(draft, action) {
|
|
725
|
+
const { chatSessionId, id, kind, createdAt, entityIds, sourceQuestionAnswerId, } = action.payload;
|
|
726
|
+
if (draft.syntheticAnswersByChatSessionId[chatSessionId] == null) {
|
|
727
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId] = [];
|
|
728
|
+
}
|
|
729
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId].push({
|
|
730
|
+
id,
|
|
731
|
+
kind,
|
|
732
|
+
entityIds: entityIds ?? [],
|
|
733
|
+
createdAt,
|
|
734
|
+
sourceQuestionAnswerId,
|
|
735
|
+
});
|
|
736
|
+
},
|
|
737
|
+
/**
|
|
738
|
+
* Update an existing synthetic answer bubble in place. Typically called
|
|
739
|
+
* when the create mutation completes: the screen transitions the bubble
|
|
740
|
+
* from `creating_*` to `created_*` and populates `entityIds` with the
|
|
741
|
+
* newly created cards / policy templates.
|
|
742
|
+
*
|
|
743
|
+
* No-op when the target bubble is missing (e.g. the session was cleared
|
|
744
|
+
* before the mutation returned).
|
|
745
|
+
*/
|
|
746
|
+
updateSyntheticAiCfoAnswer(draft, action) {
|
|
747
|
+
const { chatSessionId, id, kind, entityIds } = action.payload;
|
|
748
|
+
const list = draft.syntheticAnswersByChatSessionId[chatSessionId];
|
|
749
|
+
if (list == null) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const entry = list.find((item) => item.id === id);
|
|
753
|
+
if (entry == null) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
entry.kind = kind;
|
|
757
|
+
if (entityIds != null) {
|
|
758
|
+
entry.entityIds = entityIds;
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
/**
|
|
762
|
+
* Remove a single synthetic answer bubble (e.g. when the user manually
|
|
763
|
+
* dismisses it or the mutation errors out and we want to fall back to
|
|
764
|
+
* an inline error snackbar).
|
|
765
|
+
*/
|
|
766
|
+
removeSyntheticAiCfoAnswer(draft, action) {
|
|
767
|
+
const { chatSessionId, id } = action.payload;
|
|
768
|
+
const list = draft.syntheticAnswersByChatSessionId[chatSessionId];
|
|
769
|
+
if (list == null) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId] = list.filter((item) => item.id !== id);
|
|
773
|
+
},
|
|
774
|
+
clearSyntheticAiCfoAnswers(draft, action) {
|
|
775
|
+
delete draft.syntheticAnswersByChatSessionId[action.payload];
|
|
776
|
+
},
|
|
461
777
|
clearSession(draft, action) {
|
|
462
778
|
const sessionId = action.payload;
|
|
463
779
|
delete draft.aiCfoByChatSessionId[sessionId];
|
|
464
|
-
|
|
780
|
+
delete draft.syntheticAnswersByChatSessionId[sessionId];
|
|
465
781
|
if (draft.partialQuestionAnswers?.[sessionId] != null) {
|
|
466
782
|
draft.partialQuestionAnswers[sessionId] = undefined;
|
|
467
783
|
}
|
|
@@ -469,6 +785,7 @@ const aiCfo = (0, toolkit_1.createSlice)({
|
|
|
469
785
|
clearAiCfo(draft) {
|
|
470
786
|
draft.aiCfoByChatSessionId = {};
|
|
471
787
|
draft.partialQuestionAnswers = {};
|
|
788
|
+
draft.syntheticAnswersByChatSessionId = {};
|
|
472
789
|
},
|
|
473
790
|
deleteQuestion(draft, action) {
|
|
474
791
|
const { chatSessionId, questionAnswerId } = action.payload;
|
|
@@ -481,6 +798,12 @@ const aiCfo = (0, toolkit_1.createSlice)({
|
|
|
481
798
|
if (idx !== -1) {
|
|
482
799
|
session.questionAnswers.splice(idx, 1);
|
|
483
800
|
}
|
|
801
|
+
// Drop synthetic bubbles whose sourceQuestionAnswerId is the deleted Q/A; otherwise "Creating…" / "Created" tiles outlive their Q/A.
|
|
802
|
+
const syntheticAnswers = draft.syntheticAnswersByChatSessionId[chatSessionId];
|
|
803
|
+
if (syntheticAnswers != null) {
|
|
804
|
+
draft.syntheticAnswersByChatSessionId[chatSessionId] =
|
|
805
|
+
syntheticAnswers.filter((answer) => answer.sourceQuestionAnswerId !== questionAnswerId);
|
|
806
|
+
}
|
|
484
807
|
},
|
|
485
808
|
},
|
|
486
809
|
});
|
|
@@ -493,5 +816,5 @@ const toChatSession = (chatSessionPayload) => {
|
|
|
493
816
|
createdAt: (0, zeniDayJS_1.date)(created_at),
|
|
494
817
|
};
|
|
495
818
|
};
|
|
496
|
-
_a = aiCfo.actions, exports.setNewSession = _a.setNewSession, exports.clearSession = _a.clearSession, exports.setSessions = _a.setSessions, exports.setChatHistory = _a.setChatHistory, exports.upsertAnswerPayload = _a.upsertAnswerPayload, exports.upsertOrAddQuestionAnswerPayload = _a.upsertOrAddQuestionAnswerPayload, exports.addQuestionPayload = _a.addQuestionPayload, exports.clearAiCfo = _a.clearAiCfo, exports.deleteQuestion = _a.deleteQuestion;
|
|
819
|
+
_a = aiCfo.actions, exports.setNewSession = _a.setNewSession, exports.clearSession = _a.clearSession, exports.setSessions = _a.setSessions, exports.setChatHistory = _a.setChatHistory, exports.upsertAnswerPayload = _a.upsertAnswerPayload, exports.upsertOrAddQuestionAnswerPayload = _a.upsertOrAddQuestionAnswerPayload, exports.addQuestionPayload = _a.addQuestionPayload, exports.clearAiCfo = _a.clearAiCfo, exports.deleteQuestion = _a.deleteQuestion, exports.updateAiCfoAnswerCardPolicyWizardPlan = _a.updateAiCfoAnswerCardPolicyWizardPlan, exports.appendSyntheticAiCfoAnswer = _a.appendSyntheticAiCfoAnswer, exports.updateSyntheticAiCfoAnswer = _a.updateSyntheticAiCfoAnswer, exports.removeSyntheticAiCfoAnswer = _a.removeSyntheticAiCfoAnswer, exports.clearSyntheticAiCfoAnswers = _a.clearSyntheticAiCfoAnswers;
|
|
497
820
|
exports.default = aiCfo.reducer;
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { ID } from '../../commonStateTypes/common';
|
|
2
|
-
import { AiCfoQuestionWithAnswer, AiCfoState, ChatSession } from './aiCfoState';
|
|
2
|
+
import { AiCfoQuestionWithAnswer, AiCfoState, ChatSession, SyntheticAiCfoAnswer } from './aiCfoState';
|
|
3
3
|
export declare function getAllQuestionAnswersForChatSession(state: AiCfoState, chatSessionId: ID): AiCfoQuestionWithAnswer[];
|
|
4
4
|
export declare function getQuestionAnswerByIdForChatSession(state: AiCfoState, chatSessionId: ID, questionAnswerId: ID): AiCfoQuestionWithAnswer | undefined;
|
|
5
5
|
export declare function getAllQuestionsForChatSession(state: AiCfoState, chatSessionId: ID): string[];
|
|
6
6
|
export interface AiCfoSelectorView {
|
|
7
7
|
allSessionsWithOrderedQuestionAnswers: ChatSessionWithOrderedQuestionAnswers[];
|
|
8
|
+
syntheticAnswersByChatSessionId: Record<ID, SyntheticAiCfoAnswer[]>;
|
|
8
9
|
}
|
|
9
10
|
export interface ChatSessionWithOrderedQuestionAnswers {
|
|
10
11
|
chatSession: ChatSession;
|
|
11
12
|
questionAnswers: AiCfoQuestionWithAnswer[];
|
|
12
13
|
}
|
|
13
14
|
export declare function getAiCfoSelectorView(state: AiCfoState): AiCfoSelectorView;
|
|
15
|
+
export declare function getSyntheticAiCfoAnswersForChatSession(state: AiCfoState, chatSessionId: ID): SyntheticAiCfoAnswer[];
|
|
16
|
+
/**
|
|
17
|
+
* Look up the synthetic answer bubble (if any) whose source AI CFO Q/A
|
|
18
|
+
* is `questionAnswerId`. Used by the in-place renderer to swap a Q/A's
|
|
19
|
+
* answer bubble with the "Creating…" / "Created" tile.
|
|
20
|
+
*
|
|
21
|
+
* Returns `undefined` when no synthetic bubble has been dispatched for
|
|
22
|
+
* the answer yet (i.e. the user hasn't submitted the interactive form),
|
|
23
|
+
* or when the chat session has no synthetic entries at all.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getSyntheticAiCfoAnswerByQuestionAnswerId(state: AiCfoState, chatSessionId: ID, questionAnswerId: ID): SyntheticAiCfoAnswer | undefined;
|
|
@@ -4,6 +4,8 @@ exports.getAllQuestionAnswersForChatSession = getAllQuestionAnswersForChatSessio
|
|
|
4
4
|
exports.getQuestionAnswerByIdForChatSession = getQuestionAnswerByIdForChatSession;
|
|
5
5
|
exports.getAllQuestionsForChatSession = getAllQuestionsForChatSession;
|
|
6
6
|
exports.getAiCfoSelectorView = getAiCfoSelectorView;
|
|
7
|
+
exports.getSyntheticAiCfoAnswersForChatSession = getSyntheticAiCfoAnswersForChatSession;
|
|
8
|
+
exports.getSyntheticAiCfoAnswerByQuestionAnswerId = getSyntheticAiCfoAnswerByQuestionAnswerId;
|
|
7
9
|
function getAllQuestionAnswersForChatSession(state, chatSessionId) {
|
|
8
10
|
const arr = state.aiCfoByChatSessionId[chatSessionId]?.questionAnswers ?? [];
|
|
9
11
|
return arr
|
|
@@ -38,5 +40,30 @@ function getAiCfoSelectorView(state) {
|
|
|
38
40
|
: [],
|
|
39
41
|
}))
|
|
40
42
|
.sort((a, b) => b.chatSession.createdAt.valueOf() - a.chatSession.createdAt.valueOf());
|
|
41
|
-
return {
|
|
43
|
+
return {
|
|
44
|
+
allSessionsWithOrderedQuestionAnswers,
|
|
45
|
+
syntheticAnswersByChatSessionId: state.syntheticAnswersByChatSessionId ?? {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function getSyntheticAiCfoAnswersForChatSession(state, chatSessionId) {
|
|
49
|
+
const arr = state.syntheticAnswersByChatSessionId?.[chatSessionId] ?? [];
|
|
50
|
+
return arr
|
|
51
|
+
.slice()
|
|
52
|
+
.sort((a, b) => a.createdAt.valueOf() - b.createdAt.valueOf());
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Look up the synthetic answer bubble (if any) whose source AI CFO Q/A
|
|
56
|
+
* is `questionAnswerId`. Used by the in-place renderer to swap a Q/A's
|
|
57
|
+
* answer bubble with the "Creating…" / "Created" tile.
|
|
58
|
+
*
|
|
59
|
+
* Returns `undefined` when no synthetic bubble has been dispatched for
|
|
60
|
+
* the answer yet (i.e. the user hasn't submitted the interactive form),
|
|
61
|
+
* or when the chat session has no synthetic entries at all.
|
|
62
|
+
*/
|
|
63
|
+
function getSyntheticAiCfoAnswerByQuestionAnswerId(state, chatSessionId, questionAnswerId) {
|
|
64
|
+
const arr = state.syntheticAnswersByChatSessionId?.[chatSessionId];
|
|
65
|
+
if (arr == null) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return arr.find((answer) => answer.sourceQuestionAnswerId === questionAnswerId);
|
|
42
69
|
}
|