@zeniai/client-epic-state 5.0.83-betaRR5 → 5.0.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/entity/aiCfo/aiCfoPayload.d.ts +4 -1
- package/lib/entity/aiCfo/aiCfoReducer.js +17 -4
- package/lib/entity/aiCfo/aiCfoState.d.ts +19 -0
- package/lib/entity/aiCfo/aiCfoState.js +8 -1
- package/lib/entity/cardPolicy/cardPolicyReducer.js +99 -9
- package/lib/entity/cardPolicy/cardPolicySelector.js +4 -0
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.d.ts +2 -3
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.js +10 -4
- package/lib/epic.d.ts +12 -15
- package/lib/epic.js +12 -15
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +18 -5
- package/lib/esm/entity/aiCfo/aiCfoState.js +7 -0
- package/lib/esm/entity/cardPolicy/cardPolicyReducer.js +99 -9
- package/lib/esm/entity/cardPolicy/cardPolicySelector.js +4 -0
- package/lib/esm/entity/cardPolicy/policyRecommendationFromUploadEpic.js +11 -5
- package/lib/esm/epic.js +12 -15
- package/lib/esm/index.js +6 -21
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +19 -138
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +0 -18
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +5 -38
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +0 -23
- package/lib/index.d.ts +7 -28
- package/lib/index.js +14 -68
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.d.ts +1 -40
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +20 -139
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.d.ts +0 -15
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +0 -20
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.d.ts +0 -28
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.d.ts +1 -7
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardReducer.js +6 -39
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.d.ts +0 -15
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardSelector.js +1 -26
- package/lib/view/spendManagement/chargeCards/issueChargeCard/issueChargeCardState.d.ts +0 -16
- package/package.json +1 -1
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.js +0 -328
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.js +0 -120
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.js +0 -60
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.js +0 -118
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.js +0 -33
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.js +0 -385
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.js +0 -34
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.js +0 -33
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.js +0 -17
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.js +0 -144
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.js +0 -148
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.js +0 -112
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.js +0 -101
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.js +0 -71
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.js +0 -34
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.js +0 -1
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/applyAiCardCreationFormDraftUpdate.js +0 -42
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +0 -69
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +0 -45
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/seedAiCardCreationFormDraftEpic.js +0 -65
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +0 -64
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.d.ts +0 -38
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.js +0 -333
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.d.ts +0 -25
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.js +0 -124
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.d.ts +0 -28
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.js +0 -64
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.d.ts +0 -17
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.js +0 -122
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.d.ts +0 -23
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.js +0 -37
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.d.ts +0 -38
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.js +0 -391
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.d.ts +0 -11
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.js +0 -38
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.d.ts +0 -24
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.js +0 -40
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.d.ts +0 -253
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.js +0 -21
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.d.ts +0 -66
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.js +0 -151
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.d.ts +0 -30
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.js +0 -152
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.d.ts +0 -26
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.js +0 -116
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.d.ts +0 -35
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.js +0 -105
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.d.ts +0 -23
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.js +0 -75
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.d.ts +0 -15
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.js +0 -38
- package/lib/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.d.ts +0 -76
- package/lib/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.js +0 -2
- package/lib/view/spendManagement/chargeCards/issueChargeCard/applyAiCardCreationFormDraftUpdate.d.ts +0 -7
- package/lib/view/spendManagement/chargeCards/issueChargeCard/applyAiCardCreationFormDraftUpdate.js +0 -46
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.d.ts +0 -28
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +0 -73
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.d.ts +0 -23
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +0 -49
- package/lib/view/spendManagement/chargeCards/issueChargeCard/seedAiCardCreationFormDraftEpic.d.ts +0 -11
- package/lib/view/spendManagement/chargeCards/issueChargeCard/seedAiCardCreationFormDraftEpic.js +0 -69
- package/lib/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.d.ts +0 -11
- package/lib/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +0 -68
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compose the Step 5 review rows from the live Step 1–4 form state.
|
|
3
|
-
*
|
|
4
|
-
* - One row per `suggestedPoliciesSeed` entry, in the same order.
|
|
5
|
-
* - Per-row `isChecked` preserved from `prevPoliciesReview`; otherwise
|
|
6
|
-
* falls back to the seed value.
|
|
7
|
-
* - Chip groups rebuilt from the live vendor / category / card / limit
|
|
8
|
-
* slices, with per-chip `isSelected` merged from the previous build
|
|
9
|
-
* so user-removed (`+ name`) chips don't snap back when an upstream
|
|
10
|
-
* slice changes.
|
|
11
|
-
*/
|
|
12
|
-
export const deriveAiPolicyReviewRowsFromInputs = ({ vendorPolicy, categoryPolicy, cardSelections, limits, suggestedPoliciesSeed, prevPoliciesReview, }) => {
|
|
13
|
-
const prevById = new Map(prevPoliciesReview.map((row) => [row.id, row]));
|
|
14
|
-
const groups = deriveSharedChipGroups({
|
|
15
|
-
vendorPolicy,
|
|
16
|
-
categoryPolicy,
|
|
17
|
-
cardSelections,
|
|
18
|
-
limits,
|
|
19
|
-
});
|
|
20
|
-
return suggestedPoliciesSeed.map((seed) => {
|
|
21
|
-
const previous = prevById.get(seed.id);
|
|
22
|
-
return {
|
|
23
|
-
id: seed.id,
|
|
24
|
-
name: seed.name,
|
|
25
|
-
isChecked: previous?.isChecked ?? seed.isChecked,
|
|
26
|
-
chipGroups: previous != null
|
|
27
|
-
? mergeChipSelections(groups, previous.chipGroups)
|
|
28
|
-
: groups,
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
};
|
|
32
|
-
const mergeChipSelections = (next, prev) => {
|
|
33
|
-
const prevByGroupId = new Map(prev.map((group) => [group.id, group]));
|
|
34
|
-
return next.map((group) => {
|
|
35
|
-
const prevGroup = prevByGroupId.get(group.id);
|
|
36
|
-
if (prevGroup == null) {
|
|
37
|
-
return group;
|
|
38
|
-
}
|
|
39
|
-
const prevSelectedById = new Map(prevGroup.chips.map((chip) => [chip.id, chip.isSelected]));
|
|
40
|
-
return {
|
|
41
|
-
...group,
|
|
42
|
-
chips: group.chips.map((chip) => {
|
|
43
|
-
const prevSelected = prevSelectedById.get(chip.id);
|
|
44
|
-
return prevSelected === undefined
|
|
45
|
-
? chip
|
|
46
|
-
: { ...chip, isSelected: prevSelected };
|
|
47
|
-
}),
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
const deriveSharedChipGroups = ({ vendorPolicy, categoryPolicy, cardSelections, limits, }) => {
|
|
52
|
-
const groups = [];
|
|
53
|
-
const vendorChips = chipsFromAllowBlock(vendorPolicy);
|
|
54
|
-
if (vendorChips.length > 0) {
|
|
55
|
-
groups.push({
|
|
56
|
-
id: 'vendors',
|
|
57
|
-
label: vendorPolicy.mode === 'allow' ? 'Allowed vendors' : 'Blocked vendors',
|
|
58
|
-
chips: vendorChips,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
const categoryChips = chipsFromAllowBlock(categoryPolicy);
|
|
62
|
-
if (categoryChips.length > 0) {
|
|
63
|
-
groups.push({
|
|
64
|
-
id: 'categories',
|
|
65
|
-
label: categoryPolicy.mode === 'allow'
|
|
66
|
-
? 'Allowed categories'
|
|
67
|
-
: 'Blocked categories',
|
|
68
|
-
chips: categoryChips,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
const cardChips = cardSelections
|
|
72
|
-
.filter((row) => row.isChecked)
|
|
73
|
-
.map((row) => ({
|
|
74
|
-
id: row.id,
|
|
75
|
-
label: row.owner.displayName,
|
|
76
|
-
chipKind: 'avatar',
|
|
77
|
-
}));
|
|
78
|
-
if (cardChips.length > 0) {
|
|
79
|
-
groups.push({ id: 'cardholders', label: 'Cardholders', chips: cardChips });
|
|
80
|
-
}
|
|
81
|
-
const limitChips = limits.map((row) => ({
|
|
82
|
-
id: limitChipIdForRowId(row.id),
|
|
83
|
-
label: limitDisplayLabelForRowId(row.id),
|
|
84
|
-
helperText: limitHelperTextForRowId(row.id),
|
|
85
|
-
placeholder: '$0',
|
|
86
|
-
chipKind: 'positive',
|
|
87
|
-
isSelected: row.isEnabled,
|
|
88
|
-
optionalCode: row.isEnabled && row.amount != null ? String(row.amount) : undefined,
|
|
89
|
-
}));
|
|
90
|
-
if (limitChips.length > 0) {
|
|
91
|
-
groups.push({
|
|
92
|
-
id: 'limits',
|
|
93
|
-
label: 'Limits',
|
|
94
|
-
kind: 'limits',
|
|
95
|
-
chips: limitChips,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return groups;
|
|
99
|
-
};
|
|
100
|
-
const chipsFromAllowBlock = (field) => {
|
|
101
|
-
const items = field.mode === 'allow' ? field.allowItems : field.blockItems;
|
|
102
|
-
return items
|
|
103
|
-
.filter((item) => item.isSelected)
|
|
104
|
-
.map((item) => ({
|
|
105
|
-
id: item.id,
|
|
106
|
-
label: item.optionalCode != null
|
|
107
|
-
? `${item.label} (${item.optionalCode})`
|
|
108
|
-
: item.label,
|
|
109
|
-
chipKind: field.mode === 'allow' ? 'positive' : 'negative',
|
|
110
|
-
}));
|
|
111
|
-
};
|
|
112
|
-
const limitChipIdForRowId = (rowId) => {
|
|
113
|
-
if (rowId === 'transactionLimit') {
|
|
114
|
-
return 'limit-transaction';
|
|
115
|
-
}
|
|
116
|
-
if (rowId === 'requireReceipt') {
|
|
117
|
-
return 'limit-receipt';
|
|
118
|
-
}
|
|
119
|
-
return rowId;
|
|
120
|
-
};
|
|
121
|
-
const limitDisplayLabelForRowId = (rowId) => {
|
|
122
|
-
if (rowId === 'transactionLimit') {
|
|
123
|
-
return 'Transaction Limit';
|
|
124
|
-
}
|
|
125
|
-
if (rowId === 'requireReceipt') {
|
|
126
|
-
return 'Require Receipt';
|
|
127
|
-
}
|
|
128
|
-
return humanizeLimitId(rowId);
|
|
129
|
-
};
|
|
130
|
-
const limitHelperTextForRowId = (rowId) => {
|
|
131
|
-
if (rowId === 'transactionLimit') {
|
|
132
|
-
return 'Maximum amount per single transaction';
|
|
133
|
-
}
|
|
134
|
-
if (rowId === 'requireReceipt') {
|
|
135
|
-
return 'Require a receipt above this amount';
|
|
136
|
-
}
|
|
137
|
-
return undefined;
|
|
138
|
-
};
|
|
139
|
-
const humanizeLimitId = (id) => {
|
|
140
|
-
const spaced = id
|
|
141
|
-
.replace(/([A-Z])/g, ' $1')
|
|
142
|
-
.replace(/[_-]+/g, ' ')
|
|
143
|
-
.trim();
|
|
144
|
-
if (spaced.length === 0) {
|
|
145
|
-
return id;
|
|
146
|
-
}
|
|
147
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
148
|
-
};
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { EMPTY, concat, of } from 'rxjs';
|
|
2
|
-
import { distinctUntilChanged, filter, map, mergeMap, take, } from 'rxjs/operators';
|
|
3
|
-
import { upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, } from '../../../../../entity/aiCfo/aiCfoReducer';
|
|
4
|
-
import { getQuestionAnswerByIdForChatSession } from '../../../../../entity/aiCfo/aiCfoSelector';
|
|
5
|
-
import { fetchCardPolicyMccCategories } from '../../../../../entity/cardPolicy/cardPolicyReducer';
|
|
6
|
-
import { getAllChargeCardsWithUser } from '../../chargeCardList/chargeCardListSelector';
|
|
7
|
-
import { clearAiCardPolicyFormDraft, seedAiCardPolicyFormDraft, updateCreateCardPolicyTemplateRequestState, } from './createCardPolicyReducer';
|
|
8
|
-
const isClosedChargeCardStatusCode = (code) => code === 'closed' || code === 'closed_by_customer';
|
|
9
|
-
const toCardPolicyCardSelectionRow = (card) => {
|
|
10
|
-
const user = card.cardHolderUser;
|
|
11
|
-
const displayName = `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim() ||
|
|
12
|
-
user.email ||
|
|
13
|
-
user.userId;
|
|
14
|
-
return {
|
|
15
|
-
id: card.id,
|
|
16
|
-
isChecked: false,
|
|
17
|
-
cardName: card.cardName,
|
|
18
|
-
last4: card.accountLast4,
|
|
19
|
-
limitAmount: card.limit?.amount ?? 0,
|
|
20
|
-
limitCadence: card.creditLimitFrequency?.code,
|
|
21
|
-
owner: {
|
|
22
|
-
userId: user.userId,
|
|
23
|
-
displayName,
|
|
24
|
-
roleLabel: card.cardHolderRoles?.titleAtCompany,
|
|
25
|
-
},
|
|
26
|
-
utilizationAmount: (card.limit?.amount ?? 0) - (card.available?.amount ?? 0),
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
const readCardsMasterList = (state) => {
|
|
30
|
-
const view = getAllChargeCardsWithUser(state);
|
|
31
|
-
return view.cards
|
|
32
|
-
.filter((card) => !isClosedChargeCardStatusCode(card.status?.code))
|
|
33
|
-
.map(toCardPolicyCardSelectionRow);
|
|
34
|
-
};
|
|
35
|
-
const readMccCategoriesAvailable = (state) => state.cardPolicyState.mccCategoriesFetchState === 'Completed';
|
|
36
|
-
const readCardPolicyPayload = (state, chatSessionId, questionAnswerId) => {
|
|
37
|
-
const qa = getQuestionAnswerByIdForChatSession(state.aiCfoState, chatSessionId, questionAnswerId);
|
|
38
|
-
const visualization = qa?.responses.answer?.content.visualization;
|
|
39
|
-
if (visualization?.type !== 'interactive_form') {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const data = visualization.data;
|
|
43
|
-
if (!('formType' in data) || data.formType !== 'card_policy') {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
return data.payload;
|
|
47
|
-
};
|
|
48
|
-
const resolveQuestionAnswerId = (state, action) => {
|
|
49
|
-
if (action.payload.questionAnswerId != null) {
|
|
50
|
-
return action.payload.questionAnswerId;
|
|
51
|
-
}
|
|
52
|
-
const session = state.aiCfoState.aiCfoByChatSessionId[action.payload.chatSessionId];
|
|
53
|
-
return session?.questionAnswers.slice(-1)[0]?.questionAnswerId;
|
|
54
|
-
};
|
|
55
|
-
const isSeedTrigger = (action) => upsertAnswerPayload.match(action) ||
|
|
56
|
-
upsertOrAddQuestionAnswerPayload.match(action);
|
|
57
|
-
const isCreateSuccess = (action) => updateCreateCardPolicyTemplateRequestState.match(action) &&
|
|
58
|
-
action.payload.fetchState === 'Completed';
|
|
59
|
-
const buildSeedAction = (state, payload) => {
|
|
60
|
-
const cardsMasterList = readCardsMasterList(state);
|
|
61
|
-
const mccCategories = state.cardPolicyState.mccCategories;
|
|
62
|
-
return seedAiCardPolicyFormDraft({
|
|
63
|
-
initialData: payload,
|
|
64
|
-
cardsMasterList,
|
|
65
|
-
mccCategories,
|
|
66
|
-
suggestedAllowMerchants: state.cardPolicyState.suggestedAllowMerchants,
|
|
67
|
-
suggestedBlockMerchants: state.cardPolicyState.suggestedBlockMerchants,
|
|
68
|
-
suggestedAllowCategories: state.cardPolicyState.suggestedAllowCategories,
|
|
69
|
-
suggestedBlockCategories: state.cardPolicyState.suggestedBlockCategories,
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
/**
|
|
73
|
-
* Listens for AI CFO `interactive_form` answers with
|
|
74
|
-
* `formType === "card_policy"` and seeds the AI card-policy form
|
|
75
|
-
* draft + companion context onto `createCardPolicy` state. Mirrors
|
|
76
|
-
* `seedAiCardCreationFormDraftEpic` for the cards form.
|
|
77
|
-
*
|
|
78
|
-
* Also clears the draft on a successful bulk create so a stale draft
|
|
79
|
-
* doesn't leak into the next AI session.
|
|
80
|
-
*
|
|
81
|
-
* MCC categories drive category chip labels in the seed. When the
|
|
82
|
-
* categories slice isn't loaded yet, the epic kicks off
|
|
83
|
-
* `fetchCardPolicyMccCategories` and waits for `Completed` before
|
|
84
|
-
* dispatching the seed (mirrors `fetchUserListByType` in the cards
|
|
85
|
-
* variant).
|
|
86
|
-
*/
|
|
87
|
-
export const seedAiCardPolicyFormDraftEpic = (actions$, state$) => actions$.pipe(mergeMap((action) => {
|
|
88
|
-
if (isCreateSuccess(action)) {
|
|
89
|
-
return of(clearAiCardPolicyFormDraft());
|
|
90
|
-
}
|
|
91
|
-
if (!isSeedTrigger(action)) {
|
|
92
|
-
return EMPTY;
|
|
93
|
-
}
|
|
94
|
-
const state = state$.value;
|
|
95
|
-
const questionAnswerId = resolveQuestionAnswerId(state, action);
|
|
96
|
-
if (questionAnswerId == null) {
|
|
97
|
-
return EMPTY;
|
|
98
|
-
}
|
|
99
|
-
const payload = readCardPolicyPayload(state, action.payload.chatSessionId, questionAnswerId);
|
|
100
|
-
if (payload == null) {
|
|
101
|
-
return EMPTY;
|
|
102
|
-
}
|
|
103
|
-
if (readMccCategoriesAvailable(state)) {
|
|
104
|
-
return of(buildSeedAction(state, payload));
|
|
105
|
-
}
|
|
106
|
-
const seedWhenMccLoaded$ = state$.pipe(map((rs) => readMccCategoriesAvailable(rs)), distinctUntilChanged(), filter((available) => available), take(1), map(() => buildSeedAction(state$.value, payload)));
|
|
107
|
-
const mccFetchState = state.cardPolicyState.mccCategoriesFetchState;
|
|
108
|
-
if (mccFetchState === 'In-Progress') {
|
|
109
|
-
return seedWhenMccLoaded$;
|
|
110
|
-
}
|
|
111
|
-
return concat(of(fetchCardPolicyMccCategories()), seedWhenMccLoaded$);
|
|
112
|
-
}));
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION, } from './cardPolicyFormDraftTypes';
|
|
2
|
-
import { REVIEW_GROUP_IDS, REVIEW_LIMIT_CHIP_IDS, amountFromLimitRow, collectCategoryCodes, collectMerchantNames, } from './cardPolicyRequestParts';
|
|
3
|
-
const isSelectedChip = (chip) => chip.isSelected !== false;
|
|
4
|
-
const chipsInGroup = (row, groupId) => {
|
|
5
|
-
const group = row.chipGroups.find((g) => g.id === groupId);
|
|
6
|
-
if (group == null) {
|
|
7
|
-
return [];
|
|
8
|
-
}
|
|
9
|
-
return group.chips.filter(isSelectedChip);
|
|
10
|
-
};
|
|
11
|
-
const collectVendorLabelsFromReviewRow = (row, groupId) => chipsInGroup(row, groupId).map((chip) => chip.label);
|
|
12
|
-
const collectCategoryCodesFromReviewRow = (row, groupId) => chipsInGroup(row, groupId).map((chip) => chip.optionalCode ?? chip.id);
|
|
13
|
-
const collectCardIdsFromReviewRow = (row) => chipsInGroup(row, REVIEW_GROUP_IDS.appliedToCards).flatMap((chip) => chip.cardIds ?? []);
|
|
14
|
-
const limitValueFromReviewRow = (row, chipId) => {
|
|
15
|
-
const group = row.chipGroups.find((g) => g.id === REVIEW_GROUP_IDS.limits);
|
|
16
|
-
if (group == null) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
const chip = group.chips.find((c) => c.id === chipId && isSelectedChip(c));
|
|
20
|
-
if (chip == null) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
if (chip.optionalCode != null) {
|
|
24
|
-
const parsed = Number(chip.optionalCode);
|
|
25
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
26
|
-
}
|
|
27
|
-
const labelDigits = chip.label.match(/\d+(?:\.\d+)?/);
|
|
28
|
-
if (labelDigits == null) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
const parsed = Number(labelDigits[0]);
|
|
32
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
33
|
-
};
|
|
34
|
-
const buildGuidedEntry = (row, draft, guidedMode, description) => ({
|
|
35
|
-
allowedEntity: {
|
|
36
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'allow'),
|
|
37
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'allow'),
|
|
38
|
-
},
|
|
39
|
-
applyToCards: draft.cardSelections
|
|
40
|
-
.filter((card) => card.isChecked)
|
|
41
|
-
.map((card) => card.id),
|
|
42
|
-
blockedEntity: {
|
|
43
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'block'),
|
|
44
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'block'),
|
|
45
|
-
},
|
|
46
|
-
description: description.trim(),
|
|
47
|
-
mode: guidedMode,
|
|
48
|
-
name: row.name.trim(),
|
|
49
|
-
requiredReceiptThreshold: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT),
|
|
50
|
-
spendLimits: {
|
|
51
|
-
transaction: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION),
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
const buildAutoEntry = (row, description) => ({
|
|
55
|
-
allowedEntity: {
|
|
56
|
-
categoryCodes: collectCategoryCodesFromReviewRow(row, REVIEW_GROUP_IDS.allowedCategories),
|
|
57
|
-
merchantNames: collectVendorLabelsFromReviewRow(row, REVIEW_GROUP_IDS.allowedVendors),
|
|
58
|
-
},
|
|
59
|
-
applyToCards: collectCardIdsFromReviewRow(row),
|
|
60
|
-
blockedEntity: {
|
|
61
|
-
categoryCodes: collectCategoryCodesFromReviewRow(row, REVIEW_GROUP_IDS.restrictedCategories),
|
|
62
|
-
merchantNames: collectVendorLabelsFromReviewRow(row, REVIEW_GROUP_IDS.blockedVendors),
|
|
63
|
-
},
|
|
64
|
-
description: description.trim(),
|
|
65
|
-
name: row.name.trim(),
|
|
66
|
-
});
|
|
67
|
-
const buildUploadEntry = (row, draft, description) => ({
|
|
68
|
-
allowedEntity: {
|
|
69
|
-
categoryCodes: collectCategoryCodesFromReviewRow(row, REVIEW_GROUP_IDS.allowedCategories),
|
|
70
|
-
merchantNames: collectVendorLabelsFromReviewRow(row, REVIEW_GROUP_IDS.allowedVendors),
|
|
71
|
-
},
|
|
72
|
-
applyToCards: draft.cardSelections
|
|
73
|
-
.filter((card) => card.isChecked)
|
|
74
|
-
.map((card) => card.id),
|
|
75
|
-
blockedEntity: {
|
|
76
|
-
categoryCodes: collectCategoryCodesFromReviewRow(row, REVIEW_GROUP_IDS.restrictedCategories),
|
|
77
|
-
merchantNames: collectVendorLabelsFromReviewRow(row, REVIEW_GROUP_IDS.blockedVendors),
|
|
78
|
-
},
|
|
79
|
-
description: description.trim(),
|
|
80
|
-
name: row.name.trim(),
|
|
81
|
-
requiredReceiptThreshold: limitValueFromReviewRow(row, REVIEW_LIMIT_CHIP_IDS.receipt),
|
|
82
|
-
spendLimits: {
|
|
83
|
-
transaction: limitValueFromReviewRow(row, REVIEW_LIMIT_CHIP_IDS.transaction),
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
/**
|
|
87
|
-
* Build the array of per-row entries for the bulk
|
|
88
|
-
* `createCardPolicyTemplates` action from the AI CFO form draft.
|
|
89
|
-
* Iterates checked review rows and emits one entry per row, with the
|
|
90
|
-
* per-mode key contract enforced internally.
|
|
91
|
-
*/
|
|
92
|
-
export const toBulkCardPolicyTemplateRequestsFromDraft = ({ draft, description = '', guidedMode = 'strict', }) => {
|
|
93
|
-
const checkedRows = draft.policiesReview.filter((row) => row.isChecked);
|
|
94
|
-
if (draft.policyKind === 'guided') {
|
|
95
|
-
return checkedRows.map((row) => buildGuidedEntry(row, draft, guidedMode, description));
|
|
96
|
-
}
|
|
97
|
-
if (draft.policyKind === 'upload') {
|
|
98
|
-
return checkedRows.map((row) => buildUploadEntry(row, draft, description));
|
|
99
|
-
}
|
|
100
|
-
return checkedRows.map((row) => buildAutoEntry(row, description));
|
|
101
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION, } from './cardPolicyFormDraftTypes';
|
|
2
|
-
import { amountFromLimitRow, collectCategoryCodes, collectMerchantNames, projectAutoReviewRowToCardPolicyRequestParts, } from './cardPolicyRequestParts';
|
|
3
|
-
/**
|
|
4
|
-
* Map the AI CFO `CardPolicyFormDraft` to the wire-ready
|
|
5
|
-
* `CreateCardPolicyTemplateRequest`. Branches on `draft.policyKind`:
|
|
6
|
-
*
|
|
7
|
-
* - `auto` / `upload`: active review row's chip groups are canonical;
|
|
8
|
-
* project via `projectAutoReviewRowToCardPolicyRequestParts`. Active
|
|
9
|
-
* row = "first checked, fallback to first".
|
|
10
|
-
* - `guided`: source slices are canonical (the
|
|
11
|
-
* `applyAiCardPolicyFormDraftUpdate` reducer mirrors review-chip
|
|
12
|
-
* edits back into them); read directly from the source slices.
|
|
13
|
-
*
|
|
14
|
-
* Name picking: first checked row of `policiesReview`, fallback to the
|
|
15
|
-
* first row, fallback to empty.
|
|
16
|
-
*/
|
|
17
|
-
export const toCardPolicyTemplateRequestFromDraft = ({ draft, description = '', mode = 'strict', }) => {
|
|
18
|
-
const name = pickPolicyName(draft);
|
|
19
|
-
const base = {
|
|
20
|
-
description: description.trim(),
|
|
21
|
-
mode,
|
|
22
|
-
name: name.trim(),
|
|
23
|
-
};
|
|
24
|
-
if (draft.policyKind === 'auto' || draft.policyKind === 'upload') {
|
|
25
|
-
const activeRow = draft.policiesReview.find((row) => row.isChecked) ??
|
|
26
|
-
draft.policiesReview[0];
|
|
27
|
-
if (activeRow == null) {
|
|
28
|
-
return {
|
|
29
|
-
...base,
|
|
30
|
-
allowedEntity: { categoryCodes: [], merchantNames: [] },
|
|
31
|
-
applyToCards: [],
|
|
32
|
-
blockedEntity: { categoryCodes: [], merchantNames: [] },
|
|
33
|
-
requiredReceiptThreshold: null,
|
|
34
|
-
spendLimits: { transaction: null },
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
...base,
|
|
39
|
-
...projectAutoReviewRowToCardPolicyRequestParts(activeRow),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
// Guided
|
|
43
|
-
return {
|
|
44
|
-
...base,
|
|
45
|
-
allowedEntity: {
|
|
46
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'allow'),
|
|
47
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'allow'),
|
|
48
|
-
},
|
|
49
|
-
applyToCards: draft.cardSelections
|
|
50
|
-
.filter((row) => row.isChecked)
|
|
51
|
-
.map((row) => row.id),
|
|
52
|
-
blockedEntity: {
|
|
53
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'block'),
|
|
54
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'block'),
|
|
55
|
-
},
|
|
56
|
-
requiredReceiptThreshold: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT),
|
|
57
|
-
spendLimits: {
|
|
58
|
-
transaction: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION),
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
const pickPolicyName = (draft) => {
|
|
63
|
-
const checked = draft.policiesReview.find((row) => row.isChecked);
|
|
64
|
-
if (checked != null) {
|
|
65
|
-
return checked.name;
|
|
66
|
-
}
|
|
67
|
-
if (draft.policiesReview.length > 0) {
|
|
68
|
-
return draft.policiesReview[0].name;
|
|
69
|
-
}
|
|
70
|
-
return '';
|
|
71
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION, } from './cardPolicyFormDraftTypes';
|
|
2
|
-
import { amountFromLimitRow, collectCategoryCodes, collectMerchantNames, } from './cardPolicyRequestParts';
|
|
3
|
-
/**
|
|
4
|
-
* Map the manual `CardPolicyCreateFormDraft` onto the wire-ready
|
|
5
|
-
* `CreateCardPolicyTemplateRequest` payload consumed by the
|
|
6
|
-
* `createCardPolicyTemplates` epic.
|
|
7
|
-
*
|
|
8
|
-
* Mirrors the AI guided builder minus the `policiesReview` projection:
|
|
9
|
-
* - `vendorPolicy` / `categoryPolicy` are read directly; `mode` is
|
|
10
|
-
* forwarded from the form.
|
|
11
|
-
* - `applyToCards` is the id list of checked rows in `cardSelections`.
|
|
12
|
-
* - `spendLimits.transaction` / `requiredReceiptThreshold` are `null`
|
|
13
|
-
* when their toggle is off (BE: "not configured").
|
|
14
|
-
*/
|
|
15
|
-
export const toManualCardPolicyTemplateRequestFromDraft = (draft) => ({
|
|
16
|
-
allowedEntity: {
|
|
17
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'allow'),
|
|
18
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'allow'),
|
|
19
|
-
},
|
|
20
|
-
applyToCards: draft.cardSelections
|
|
21
|
-
.filter((row) => row.isChecked)
|
|
22
|
-
.map((row) => row.id),
|
|
23
|
-
blockedEntity: {
|
|
24
|
-
categoryCodes: collectCategoryCodes(draft.categoryPolicy, 'block'),
|
|
25
|
-
merchantNames: collectMerchantNames(draft.vendorPolicy, 'block'),
|
|
26
|
-
},
|
|
27
|
-
description: draft.description.trim(),
|
|
28
|
-
mode: draft.mode,
|
|
29
|
-
name: draft.name.trim(),
|
|
30
|
-
requiredReceiptThreshold: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT),
|
|
31
|
-
spendLimits: {
|
|
32
|
-
transaction: amountFromLimitRow(draft.limits, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION),
|
|
33
|
-
},
|
|
34
|
-
});
|
package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { deriveAiCardRowsFromTeams } from './deriveAiCardRowsFromTeams';
|
|
2
|
-
// Single source of truth for "what the next form draft should look like
|
|
3
|
-
// after a watchHandler flush".
|
|
4
|
-
//
|
|
5
|
-
// `create` mode + missing context: keep the payload as-is.
|
|
6
|
-
//
|
|
7
|
-
// `review` mode: re-derive step-2 `cardRows` from the latest `teamRows`
|
|
8
|
-
// ONLY when the step-1 selection changed (a team was toggled on/off).
|
|
9
|
-
// Step-2-only edits (dropdown swap, owner change, limit edit) must skip
|
|
10
|
-
// the re-derive because `deriveAiCardRowsFromTeams` matches previous rows
|
|
11
|
-
// by `cardRow.id`, which the dropdown swap has just mutated — running it
|
|
12
|
-
// on a step-2-only diff reverts the user's edit back to the source team's
|
|
13
|
-
// original id/label.
|
|
14
|
-
export const applyAiCardCreationFormDraftUpdate = ({ next, prev, context, }) => {
|
|
15
|
-
if (context == null || context.mode === 'create') {
|
|
16
|
-
return next;
|
|
17
|
-
}
|
|
18
|
-
if (!haveCheckedTeamRowsChanged(prev?.teamRows, next.teamRows)) {
|
|
19
|
-
return next;
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
teamRows: next.teamRows,
|
|
23
|
-
cardRows: deriveAiCardRowsFromTeams({
|
|
24
|
-
teamRows: next.teamRows,
|
|
25
|
-
prevCardRows: next.cardRows,
|
|
26
|
-
allUsers: context.allUsers,
|
|
27
|
-
suggestedOwnerUserIdByTeamId: context.suggestedOwnerUserIdByTeamId,
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
const haveCheckedTeamRowsChanged = (prevTeamRows, nextTeamRows) => {
|
|
32
|
-
const prevCheckedIds = (prevTeamRows ?? [])
|
|
33
|
-
.filter((row) => row.isChecked)
|
|
34
|
-
.map((row) => row.id);
|
|
35
|
-
const nextCheckedIds = nextTeamRows
|
|
36
|
-
.filter((row) => row.isChecked)
|
|
37
|
-
.map((row) => row.id);
|
|
38
|
-
if (prevCheckedIds.length !== nextCheckedIds.length) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
return prevCheckedIds.some((id, idx) => id !== nextCheckedIds[idx]);
|
|
42
|
-
};
|
package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { deriveAiCardRowsFromTeams } from './deriveAiCardRowsFromTeams';
|
|
2
|
-
/**
|
|
3
|
-
* Build the initial `CardsFormDraft` + companion seed context for a
|
|
4
|
-
* fresh AI card-creation session. Used by the `seedAiCardCreationFormDraft`
|
|
5
|
-
* reducer.
|
|
6
|
-
*
|
|
7
|
-
* Branches on `initialData.mode`:
|
|
8
|
-
* - `review`: step-1 team rows start fully checked and step-2 `cardRows`
|
|
9
|
-
* are seeded by `deriveAiCardRowsFromTeams` so the table is populated
|
|
10
|
-
* on first render. `applyAiCardCreationFormDraftUpdate` only re-derives
|
|
11
|
-
* when a team is toggled in step 1, so without this initial seed the
|
|
12
|
-
* happy path (user proceeds to step 2 without un/re-checking) would
|
|
13
|
-
* land on an empty table.
|
|
14
|
-
* - `create`: no step-1 list — seed `cardRows` directly from the AI's
|
|
15
|
-
* suggested cards (resolving owners via `allUsers`) and leave
|
|
16
|
-
* `teamRows` empty.
|
|
17
|
-
*/
|
|
18
|
-
export const buildAiCardCreationFormDraftSeed = ({ initialData, allUsers, }) => {
|
|
19
|
-
const context = {
|
|
20
|
-
allUsers,
|
|
21
|
-
mode: initialData.mode,
|
|
22
|
-
suggestedOwnerUserIdByTeamId: initialData.suggestedOwnerUserIdByTeamId,
|
|
23
|
-
};
|
|
24
|
-
if (initialData.mode === 'create') {
|
|
25
|
-
return {
|
|
26
|
-
context,
|
|
27
|
-
draft: {
|
|
28
|
-
teamRows: [],
|
|
29
|
-
cardRows: initialData.cardRows.map((seed) => buildCardRowFromSeed(seed, allUsers)),
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const teamRows = initialData.teamRows.map((row) => ({
|
|
34
|
-
cardKind: row.cardKind,
|
|
35
|
-
id: row.id,
|
|
36
|
-
isChecked: true,
|
|
37
|
-
label: row.label,
|
|
38
|
-
value: row.avgMonthlySpend,
|
|
39
|
-
rightLabel: row.recommendedLimit,
|
|
40
|
-
rightSubLabel: row.recommendedLimitCadence,
|
|
41
|
-
}));
|
|
42
|
-
return {
|
|
43
|
-
context,
|
|
44
|
-
draft: {
|
|
45
|
-
teamRows,
|
|
46
|
-
cardRows: deriveAiCardRowsFromTeams({
|
|
47
|
-
teamRows,
|
|
48
|
-
prevCardRows: [],
|
|
49
|
-
allUsers,
|
|
50
|
-
suggestedOwnerUserIdByTeamId: initialData.suggestedOwnerUserIdByTeamId,
|
|
51
|
-
}),
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
const buildCardRowFromSeed = (seed, allUsers) => {
|
|
56
|
-
const selectedUser = seed.suggestedOwnerUserId != null
|
|
57
|
-
? allUsers.find((user) => user.userId === seed.suggestedOwnerUserId)
|
|
58
|
-
: undefined;
|
|
59
|
-
return {
|
|
60
|
-
cardKind: seed.cardKind,
|
|
61
|
-
id: seed.id,
|
|
62
|
-
label: seed.label,
|
|
63
|
-
owner: {
|
|
64
|
-
allUsers,
|
|
65
|
-
selectedUser,
|
|
66
|
-
},
|
|
67
|
-
recommendedLimit: seed.recommendedLimit,
|
|
68
|
-
};
|
|
69
|
-
};
|
package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Derive the step-2 `cardRows` from the user's step-1 team selection.
|
|
3
|
-
*
|
|
4
|
-
* Behaviour:
|
|
5
|
-
* - One output row per `teamRow.isChecked === true`, in the same order.
|
|
6
|
-
* - When a team is still checked AND we have a previous row for it, the
|
|
7
|
-
* previous row is preserved verbatim — so user edits made in step 2
|
|
8
|
-
* aren't blown away when an upstream watchHandler re-derives.
|
|
9
|
-
* - Newly-included teams fall back to:
|
|
10
|
-
* - `selectedUser = the User whose userId === suggestedOwnerUserIdByTeamId[teamId]`
|
|
11
|
-
* - `recommendedLimit = numeric parse of `teamRow.rightLabel` (e.g. "$20,000")`
|
|
12
|
-
*/
|
|
13
|
-
export const deriveAiCardRowsFromTeams = ({ teamRows, prevCardRows = [], allUsers, suggestedOwnerUserIdByTeamId, }) => {
|
|
14
|
-
const prevById = new Map(prevCardRows.map((row) => [row.id, row]));
|
|
15
|
-
const userById = new Map(allUsers.map((user) => [user.userId, user]));
|
|
16
|
-
return teamRows
|
|
17
|
-
.filter((row) => row.isChecked)
|
|
18
|
-
.map((teamRow) => {
|
|
19
|
-
const previous = prevById.get(teamRow.id);
|
|
20
|
-
if (previous != null) {
|
|
21
|
-
return previous;
|
|
22
|
-
}
|
|
23
|
-
const suggestedUserId = suggestedOwnerUserIdByTeamId[teamRow.id];
|
|
24
|
-
const selectedUser = suggestedUserId != null ? userById.get(suggestedUserId) : undefined;
|
|
25
|
-
return {
|
|
26
|
-
cardKind: teamRow.cardKind,
|
|
27
|
-
id: teamRow.id,
|
|
28
|
-
label: teamRow.label,
|
|
29
|
-
owner: {
|
|
30
|
-
allUsers,
|
|
31
|
-
selectedUser,
|
|
32
|
-
},
|
|
33
|
-
recommendedLimit: parseLimitToFloat(teamRow.rightLabel),
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
// Best-effort numeric parse of an AI-provided limit string like "$20,000".
|
|
38
|
-
const parseLimitToFloat = (raw) => {
|
|
39
|
-
const cleaned = raw.replace(/[^0-9.-]/g, '');
|
|
40
|
-
if (cleaned.length === 0) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
const parsed = Number(cleaned);
|
|
44
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
45
|
-
};
|