@zeniai/client-epic-state 5.0.83-betaRR3 → 5.0.83-betaRR5
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 +1 -4
- package/lib/entity/aiCfo/aiCfoReducer.js +3 -11
- package/lib/entity/aiCfo/aiCfoState.d.ts +0 -19
- package/lib/entity/aiCfo/aiCfoState.js +1 -8
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.d.ts +3 -2
- package/lib/entity/cardPolicy/policyRecommendationFromUploadEpic.js +3 -2
- package/lib/epic.d.ts +14 -12
- package/lib/epic.js +14 -12
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +4 -12
- package/lib/esm/entity/aiCfo/aiCfoState.js +0 -7
- package/lib/esm/entity/cardPolicy/policyRecommendationFromUploadEpic.js +4 -3
- package/lib/esm/epic.js +14 -12
- package/lib/esm/index.js +14 -3
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.js +328 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.js +120 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.js +60 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.js +118 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.js +33 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.js +385 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.js +34 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.js +33 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.js +17 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.js +144 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +137 -1
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +18 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.js +148 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.js +112 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.js +101 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.js +71 -0
- package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.js +34 -0
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/applyAiCardCreationFormDraftUpdate.js +0 -1
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +13 -6
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +1 -3
- package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +0 -5
- package/lib/index.d.ts +20 -4
- package/lib/index.js +45 -4
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.d.ts +38 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyAiCardPolicyFormDraftUpdate.js +333 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.d.ts +25 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToAiCardPolicyDraft.js +124 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.d.ts +28 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToDraftEpic.js +64 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.d.ts +17 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyExtractedPolicyToManualCardPolicyDraft.js +122 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.d.ts +23 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/applyUploadPlanToAiCardPolicyDraft.js +37 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.d.ts +38 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildAiCardPolicyFormDraftSeed.js +391 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.d.ts +11 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/buildManualCardPolicyFormDraftSeed.js +38 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.d.ts +24 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.js +40 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.d.ts +253 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyFormDraftTypes.js +21 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.d.ts +66 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyRequestParts.js +151 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.d.ts +40 -1
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyReducer.js +138 -2
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.d.ts +15 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicySelector.js +20 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/createCardPolicyState.d.ts +28 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.d.ts +30 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/deriveAiPolicyReviewRowsFromInputs.js +152 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.d.ts +26 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/seedAiCardPolicyFormDraftEpic.js +116 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.d.ts +35 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toBulkCardPolicyTemplateRequestsFromDraft.js +105 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.d.ts +23 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toCardPolicyTemplateRequestFromDraft.js +75 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.d.ts +15 -0
- package/lib/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/toManualCardPolicyTemplateRequestFromDraft.js +38 -0
- package/lib/view/spendManagement/chargeCards/issueChargeCard/aiCardCreationFormDraftTypes.d.ts +3 -6
- package/lib/view/spendManagement/chargeCards/issueChargeCard/applyAiCardCreationFormDraftUpdate.js +0 -1
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.d.ts +6 -3
- package/lib/view/spendManagement/chargeCards/issueChargeCard/buildAiCardCreationFormDraftSeed.js +13 -6
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.d.ts +1 -4
- package/lib/view/spendManagement/chargeCards/issueChargeCard/deriveAiCardRowsFromTeams.js +1 -3
- package/lib/view/spendManagement/chargeCards/issueChargeCard/toIssueChargeCardLocalDataFromDraft.js +0 -5
- package/package.json +1 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { buildVendorChipId, toMccCategoryChipFieldValue, toVendorChipFieldValue, } from './cardPolicyChipIds';
|
|
2
|
+
import { CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT, CARD_POLICY_LIMIT_ROW_ID_TRANSACTION, } from './cardPolicyFormDraftTypes';
|
|
3
|
+
const EMPTY_ALLOW_BLOCK = {
|
|
4
|
+
mode: 'allow',
|
|
5
|
+
allowItems: [],
|
|
6
|
+
blockItems: [],
|
|
7
|
+
};
|
|
8
|
+
const EMPTY_FORM_VALUES = {
|
|
9
|
+
vendorPolicy: EMPTY_ALLOW_BLOCK,
|
|
10
|
+
categoryPolicy: EMPTY_ALLOW_BLOCK,
|
|
11
|
+
cardSelections: [],
|
|
12
|
+
limits: [],
|
|
13
|
+
policiesReview: [],
|
|
14
|
+
};
|
|
15
|
+
export const buildEmptyLimitRows = () => [
|
|
16
|
+
{ id: CARD_POLICY_LIMIT_ROW_ID_TRANSACTION, isEnabled: false },
|
|
17
|
+
{ id: CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT, isEnabled: false },
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Build the initial `CardPolicyFormDraft` + companion seed context for
|
|
21
|
+
* a fresh AI CFO card-policy session. Used by the
|
|
22
|
+
* `seedAiCardPolicyFormDraft` reducer.
|
|
23
|
+
*
|
|
24
|
+
* - `auto` (1-step review): chip groups are built eagerly from each
|
|
25
|
+
* `draftPolicies[i]` since there's no live form to derive from.
|
|
26
|
+
* - `guided` (5-step wizard): Steps 1–4 seeded from `draftPolicies[0]`,
|
|
27
|
+
* Step 5 seeded with one identity-only row per `draftPolicies[i]`;
|
|
28
|
+
* chip groups are re-derived on every `applyAiCardPolicyFormDraftUpdate`.
|
|
29
|
+
* - `upload` (3-step): pre-recommendation returns an empty scaffold;
|
|
30
|
+
* post-recommendation, Step 5 review rows + their chip groups are
|
|
31
|
+
* derived from `wizardPlan.step5Review.draftPolicies`.
|
|
32
|
+
*/
|
|
33
|
+
export const buildAiCardPolicyFormDraftSeed = ({ initialData, cardsMasterList = [], categorySearchOptions, suggestedAllowMerchants, suggestedBlockMerchants, suggestedAllowCategories, suggestedBlockCategories, }) => {
|
|
34
|
+
const suggestedPoliciesSeed = buildSuggestedPoliciesSeed(initialData);
|
|
35
|
+
const wizardPlan = 'wizardPlan' in initialData ? initialData.wizardPlan : undefined;
|
|
36
|
+
const source = initialData.policyKind === 'upload' ? initialData.source : undefined;
|
|
37
|
+
const context = {
|
|
38
|
+
policyKind: initialData.policyKind,
|
|
39
|
+
cardsMasterList,
|
|
40
|
+
categorySearchOptions: categorySearchOptions ?? [],
|
|
41
|
+
suggestedAllowMerchants,
|
|
42
|
+
suggestedBlockMerchants,
|
|
43
|
+
suggestedAllowCategories,
|
|
44
|
+
suggestedBlockCategories,
|
|
45
|
+
suggestedPoliciesSeed,
|
|
46
|
+
wizardPlan,
|
|
47
|
+
source,
|
|
48
|
+
};
|
|
49
|
+
const draft = buildDraftFromInitialData({
|
|
50
|
+
initialData,
|
|
51
|
+
cardsMasterList,
|
|
52
|
+
categorySearchOptions,
|
|
53
|
+
suggestedAllowMerchants,
|
|
54
|
+
suggestedBlockMerchants,
|
|
55
|
+
suggestedAllowCategories,
|
|
56
|
+
suggestedBlockCategories,
|
|
57
|
+
});
|
|
58
|
+
return { draft, context };
|
|
59
|
+
};
|
|
60
|
+
const buildDraftFromInitialData = ({ initialData, cardsMasterList, categorySearchOptions, suggestedAllowMerchants, suggestedBlockMerchants, suggestedAllowCategories, suggestedBlockCategories, }) => {
|
|
61
|
+
const vendorAllowSeed = merchantsToChips(suggestedAllowMerchants);
|
|
62
|
+
const vendorBlockSeed = merchantsToChips(suggestedBlockMerchants);
|
|
63
|
+
const categoryAllowSeed = categoriesToChips(suggestedAllowCategories);
|
|
64
|
+
const categoryBlockSeed = categoriesToChips(suggestedBlockCategories);
|
|
65
|
+
if (initialData.policyKind === 'auto') {
|
|
66
|
+
const autoDraftPolicies = initialData.wizardPlan?.step5Review?.draftPolicies ?? [];
|
|
67
|
+
return {
|
|
68
|
+
...EMPTY_FORM_VALUES,
|
|
69
|
+
policyKind: 'auto',
|
|
70
|
+
policiesReview: buildAutoReviewRows(autoDraftPolicies, cardsMasterList, categorySearchOptions),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (initialData.policyKind === 'upload') {
|
|
74
|
+
const uploadDraftPolicies = initialData.wizardPlan?.step5Review?.draftPolicies;
|
|
75
|
+
if (uploadDraftPolicies == null) {
|
|
76
|
+
return {
|
|
77
|
+
...EMPTY_FORM_VALUES,
|
|
78
|
+
policyKind: 'upload',
|
|
79
|
+
cardSelections: cardsMasterList.map((row) => ({
|
|
80
|
+
...row,
|
|
81
|
+
isChecked: false,
|
|
82
|
+
})),
|
|
83
|
+
limits: buildEmptyLimitRows(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
...EMPTY_FORM_VALUES,
|
|
88
|
+
policyKind: 'upload',
|
|
89
|
+
cardSelections: cardsMasterList.map((row) => ({
|
|
90
|
+
...row,
|
|
91
|
+
isChecked: false,
|
|
92
|
+
})),
|
|
93
|
+
limits: buildEmptyLimitRows(),
|
|
94
|
+
policiesReview: buildUploadReviewRows(uploadDraftPolicies, cardsMasterList, categorySearchOptions),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Guided
|
|
98
|
+
const draftPolicies = initialData.wizardPlan?.step5Review?.draftPolicies ?? [];
|
|
99
|
+
const primary = draftPolicies[0];
|
|
100
|
+
if (primary == null) {
|
|
101
|
+
return {
|
|
102
|
+
policyKind: 'guided',
|
|
103
|
+
vendorPolicy: buildAllowBlockFromSeeds(vendorAllowSeed, vendorBlockSeed),
|
|
104
|
+
categoryPolicy: buildAllowBlockFromSeeds(categoryAllowSeed, categoryBlockSeed),
|
|
105
|
+
cardSelections: cardsMasterList.map((row) => ({
|
|
106
|
+
...row,
|
|
107
|
+
isChecked: false,
|
|
108
|
+
})),
|
|
109
|
+
limits: buildEmptyLimitRows(),
|
|
110
|
+
policiesReview: [],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const mccCodeToCategory = buildMccCodeToCategoryMap(categorySearchOptions);
|
|
114
|
+
const vendorMode = inferAllowBlockMode(primary.allowedEntity.merchantNames.length, primary.blockedEntity.merchantNames.length);
|
|
115
|
+
const aiVendorAllow = draftMerchantsToChips(primary.allowedEntity.merchantNames);
|
|
116
|
+
const aiVendorBlock = draftMerchantsToChips(primary.blockedEntity.merchantNames);
|
|
117
|
+
const vendorPolicy = {
|
|
118
|
+
mode: vendorMode,
|
|
119
|
+
allowItems: mergeAiAndSeedChips(aiVendorAllow, vendorAllowSeed),
|
|
120
|
+
blockItems: mergeAiAndSeedChips(aiVendorBlock, vendorBlockSeed),
|
|
121
|
+
};
|
|
122
|
+
const categoryMode = inferAllowBlockMode(primary.allowedEntity.categoryCodes.length, primary.blockedEntity.categoryCodes.length);
|
|
123
|
+
const aiCategoryAllow = draftCategoryCodesToChips(primary.allowedEntity.categoryCodes, mccCodeToCategory);
|
|
124
|
+
const aiCategoryBlock = draftCategoryCodesToChips(primary.blockedEntity.categoryCodes, mccCodeToCategory);
|
|
125
|
+
const categoryPolicy = {
|
|
126
|
+
mode: categoryMode,
|
|
127
|
+
allowItems: mergeAiAndSeedChips(aiCategoryAllow, categoryAllowSeed),
|
|
128
|
+
blockItems: mergeAiAndSeedChips(aiCategoryBlock, categoryBlockSeed),
|
|
129
|
+
};
|
|
130
|
+
const cardSelections = cardsMasterList.map((row) => ({
|
|
131
|
+
...row,
|
|
132
|
+
isChecked: primary.applyToCards.includes(row.id),
|
|
133
|
+
}));
|
|
134
|
+
const limits = [
|
|
135
|
+
{
|
|
136
|
+
id: CARD_POLICY_LIMIT_ROW_ID_TRANSACTION,
|
|
137
|
+
isEnabled: primary.spendLimits.transaction > 0,
|
|
138
|
+
amount: primary.spendLimits.transaction > 0
|
|
139
|
+
? primary.spendLimits.transaction
|
|
140
|
+
: undefined,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT,
|
|
144
|
+
isEnabled: primary.requiredReceiptThreshold > 0,
|
|
145
|
+
amount: primary.requiredReceiptThreshold > 0
|
|
146
|
+
? primary.requiredReceiptThreshold
|
|
147
|
+
: undefined,
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
return {
|
|
151
|
+
policyKind: 'guided',
|
|
152
|
+
vendorPolicy,
|
|
153
|
+
categoryPolicy,
|
|
154
|
+
cardSelections,
|
|
155
|
+
limits,
|
|
156
|
+
policiesReview: draftPolicies.map((p) => ({
|
|
157
|
+
id: p.id,
|
|
158
|
+
isChecked: true,
|
|
159
|
+
name: p.name,
|
|
160
|
+
chipGroups: [],
|
|
161
|
+
})),
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
const buildSuggestedPoliciesSeed = (initialData) => (initialData.wizardPlan?.step5Review?.draftPolicies ?? []).map((draft) => ({
|
|
165
|
+
id: draft.id,
|
|
166
|
+
name: draft.name,
|
|
167
|
+
isChecked: true,
|
|
168
|
+
}));
|
|
169
|
+
const merchantsToChips = (names) => (names ?? []).map((name) => toVendorChipFieldValue(name, false));
|
|
170
|
+
const categoriesToChips = (categories) => (categories ?? []).map((category) => toMccCategoryChipFieldValue(category, false));
|
|
171
|
+
const buildAllowBlockFromSeeds = (allowItems, blockItems) => {
|
|
172
|
+
if (allowItems.length === 0 && blockItems.length === 0) {
|
|
173
|
+
return EMPTY_ALLOW_BLOCK;
|
|
174
|
+
}
|
|
175
|
+
return { mode: 'allow', allowItems, blockItems };
|
|
176
|
+
};
|
|
177
|
+
const buildMccCodeToCategoryMap = (categories) => {
|
|
178
|
+
const map = new Map();
|
|
179
|
+
for (const category of categories ?? []) {
|
|
180
|
+
for (const code of category.mccCodes) {
|
|
181
|
+
if (!map.has(code)) {
|
|
182
|
+
map.set(code, category);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return map;
|
|
187
|
+
};
|
|
188
|
+
const buildOwnerSummaries = (cardIds, cardsMasterList) => {
|
|
189
|
+
const cardsById = new Map(cardsMasterList.map((card) => [card.id, card]));
|
|
190
|
+
const summaries = new Map();
|
|
191
|
+
for (const cardId of cardIds) {
|
|
192
|
+
const card = cardsById.get(cardId);
|
|
193
|
+
if (card == null) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const existing = summaries.get(card.owner.userId);
|
|
197
|
+
if (existing != null) {
|
|
198
|
+
existing.cardIds.push(cardId);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
summaries.set(card.owner.userId, {
|
|
202
|
+
userId: card.owner.userId,
|
|
203
|
+
displayName: card.owner.displayName,
|
|
204
|
+
cardIds: [cardId],
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return Array.from(summaries.values());
|
|
208
|
+
};
|
|
209
|
+
const buildAutoReviewChipGroupsForDraft = (draft, cardsMasterList, mccCodeToCategory) => {
|
|
210
|
+
const groups = [];
|
|
211
|
+
if (draft.allowedEntity.merchantNames.length > 0) {
|
|
212
|
+
groups.push({
|
|
213
|
+
id: 'allowed-vendors',
|
|
214
|
+
label: 'Allowed Vendors',
|
|
215
|
+
chips: draft.allowedEntity.merchantNames.map((name) => ({
|
|
216
|
+
id: buildVendorChipId(name),
|
|
217
|
+
label: name,
|
|
218
|
+
chipKind: 'positive',
|
|
219
|
+
isSelected: true,
|
|
220
|
+
})),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (draft.blockedEntity.merchantNames.length > 0) {
|
|
224
|
+
groups.push({
|
|
225
|
+
id: 'blocked-vendors',
|
|
226
|
+
label: 'Blocked Vendors',
|
|
227
|
+
chips: draft.blockedEntity.merchantNames.map((name) => ({
|
|
228
|
+
id: buildVendorChipId(name),
|
|
229
|
+
label: name,
|
|
230
|
+
chipKind: 'negative',
|
|
231
|
+
isSelected: true,
|
|
232
|
+
})),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (draft.allowedEntity.categoryCodes.length > 0) {
|
|
236
|
+
groups.push({
|
|
237
|
+
id: 'allowed-categories',
|
|
238
|
+
label: 'Allowed Categories',
|
|
239
|
+
chips: draft.allowedEntity.categoryCodes.map((code) => {
|
|
240
|
+
const category = mccCodeToCategory.get(code);
|
|
241
|
+
const label = category != null ? `${category.name} (${code})` : code;
|
|
242
|
+
return {
|
|
243
|
+
id: code,
|
|
244
|
+
label,
|
|
245
|
+
chipKind: 'positive',
|
|
246
|
+
isSelected: true,
|
|
247
|
+
optionalCode: code,
|
|
248
|
+
};
|
|
249
|
+
}),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (draft.blockedEntity.categoryCodes.length > 0) {
|
|
253
|
+
groups.push({
|
|
254
|
+
id: 'restricted-categories',
|
|
255
|
+
label: 'Restricted Categories',
|
|
256
|
+
chips: draft.blockedEntity.categoryCodes.map((code) => {
|
|
257
|
+
const category = mccCodeToCategory.get(code);
|
|
258
|
+
const label = category != null ? `${category.name} (${code})` : code;
|
|
259
|
+
return {
|
|
260
|
+
id: code,
|
|
261
|
+
label,
|
|
262
|
+
chipKind: 'negative',
|
|
263
|
+
isSelected: true,
|
|
264
|
+
optionalCode: code,
|
|
265
|
+
};
|
|
266
|
+
}),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (draft.applyToCards.length > 0) {
|
|
270
|
+
const owners = buildOwnerSummaries(draft.applyToCards, cardsMasterList);
|
|
271
|
+
if (owners.length > 0) {
|
|
272
|
+
groups.push({
|
|
273
|
+
id: 'applied-to-cards',
|
|
274
|
+
label: 'Applied to Cards',
|
|
275
|
+
chips: owners.map((owner) => ({
|
|
276
|
+
id: owner.userId,
|
|
277
|
+
label: owner.displayName,
|
|
278
|
+
chipKind: 'avatar',
|
|
279
|
+
cardIds: owner.cardIds,
|
|
280
|
+
isSelected: true,
|
|
281
|
+
})),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return groups;
|
|
286
|
+
};
|
|
287
|
+
const buildAutoReviewRows = (draftPolicies, cardsMasterList, categorySearchOptions) => {
|
|
288
|
+
const mccCodeToCategory = buildMccCodeToCategoryMap(categorySearchOptions);
|
|
289
|
+
return draftPolicies.map((draft) => ({
|
|
290
|
+
id: draft.id,
|
|
291
|
+
isChecked: true,
|
|
292
|
+
name: draft.name,
|
|
293
|
+
chipGroups: buildAutoReviewChipGroupsForDraft(draft, cardsMasterList, mccCodeToCategory),
|
|
294
|
+
}));
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Upload-only "Limits" chip group carrying the draft's
|
|
298
|
+
* `spend_limits.transaction` and `required_receipt_threshold`. Each
|
|
299
|
+
* chip stashes its numeric value on `optionalCode` so the bulk-submit
|
|
300
|
+
* builder can recover it without re-parsing the label.
|
|
301
|
+
*/
|
|
302
|
+
const buildUploadLimitsChipGroup = (draft) => {
|
|
303
|
+
const transactionEnabled = draft.spendLimits.transaction > 0;
|
|
304
|
+
const receiptEnabled = draft.requiredReceiptThreshold > 0;
|
|
305
|
+
return {
|
|
306
|
+
id: 'limits',
|
|
307
|
+
label: 'Limits',
|
|
308
|
+
kind: 'limits',
|
|
309
|
+
chips: [
|
|
310
|
+
{
|
|
311
|
+
id: 'limit-transaction',
|
|
312
|
+
label: 'Transaction Limit',
|
|
313
|
+
helperText: 'Maximum amount per single transaction',
|
|
314
|
+
placeholder: '$0',
|
|
315
|
+
chipKind: 'positive',
|
|
316
|
+
isSelected: transactionEnabled,
|
|
317
|
+
optionalCode: transactionEnabled
|
|
318
|
+
? String(draft.spendLimits.transaction)
|
|
319
|
+
: undefined,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: 'limit-receipt',
|
|
323
|
+
label: 'Require Receipt',
|
|
324
|
+
helperText: 'Require a receipt above this amount',
|
|
325
|
+
placeholder: '$0',
|
|
326
|
+
chipKind: 'positive',
|
|
327
|
+
isSelected: receiptEnabled,
|
|
328
|
+
optionalCode: receiptEnabled
|
|
329
|
+
? String(draft.requiredReceiptThreshold)
|
|
330
|
+
: undefined,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
export const buildUploadReviewRows = (draftPolicies, cardsMasterList, categorySearchOptions) => {
|
|
336
|
+
const mccCodeToCategory = buildMccCodeToCategoryMap(categorySearchOptions);
|
|
337
|
+
return draftPolicies.map((draft) => {
|
|
338
|
+
const chipGroups = buildAutoReviewChipGroupsForDraft(draft, cardsMasterList, mccCodeToCategory);
|
|
339
|
+
chipGroups.push(buildUploadLimitsChipGroup(draft));
|
|
340
|
+
return {
|
|
341
|
+
id: draft.id,
|
|
342
|
+
isChecked: true,
|
|
343
|
+
name: draft.name,
|
|
344
|
+
chipGroups,
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Infer the AllowBlock mode for a draft policy field. The BE doesn't
|
|
350
|
+
* tell the FE which radio to pre-select; mirrors the rule used by
|
|
351
|
+
* `applyExtractedPolicyTo*FormValues`:
|
|
352
|
+
*
|
|
353
|
+
* - allow side has any value → `"allow"`
|
|
354
|
+
* - block side has any value → `"block"`
|
|
355
|
+
* - both empty → `"allow"` (default)
|
|
356
|
+
* - both populated → `"allow"` (user can flip the radio to reveal block).
|
|
357
|
+
*/
|
|
358
|
+
const inferAllowBlockMode = (allowCount, blockCount) => {
|
|
359
|
+
if (allowCount > 0) {
|
|
360
|
+
return 'allow';
|
|
361
|
+
}
|
|
362
|
+
if (blockCount > 0) {
|
|
363
|
+
return 'block';
|
|
364
|
+
}
|
|
365
|
+
return 'allow';
|
|
366
|
+
};
|
|
367
|
+
const draftMerchantsToChips = (names) => names.map((name) => toVendorChipFieldValue(name, true));
|
|
368
|
+
const draftCategoryCodesToChips = (codes, mccCodeToCategory) => codes.map((code) => {
|
|
369
|
+
const category = mccCodeToCategory.get(code);
|
|
370
|
+
if (category != null) {
|
|
371
|
+
return toMccCategoryChipFieldValue(category, true);
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
id: code,
|
|
375
|
+
label: code,
|
|
376
|
+
isSelected: true,
|
|
377
|
+
optionalCode: code,
|
|
378
|
+
optionalCodes: [code],
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
const mergeAiAndSeedChips = (aiChips, seedChips) => {
|
|
382
|
+
const seenIds = new Set(aiChips.map((chip) => chip.id));
|
|
383
|
+
const extraSeeds = seedChips.filter((chip) => !seenIds.has(chip.id));
|
|
384
|
+
return [...aiChips, ...extraSeeds];
|
|
385
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { buildEmptyLimitRows } from './buildAiCardPolicyFormDraftSeed';
|
|
2
|
+
import { toMccCategoryChipFieldValue, toVendorChipFieldValue, } from './cardPolicyChipIds';
|
|
3
|
+
const EMPTY_ALLOW_BLOCK = {
|
|
4
|
+
mode: 'allow',
|
|
5
|
+
allowItems: [],
|
|
6
|
+
blockItems: [],
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Build the initial `CardPolicyCreateFormDraft` for a fresh manual
|
|
10
|
+
* create-page session. Mirrors the AI guided seed minus the wizard
|
|
11
|
+
* plan: every BE-suggested chip lands `isSelected: false` so the user
|
|
12
|
+
* has to opt each suggestion onto the policy explicitly.
|
|
13
|
+
*/
|
|
14
|
+
export const buildManualCardPolicyFormDraftSeed = ({ cardsMasterList = [], suggestedAllowMerchants, suggestedBlockMerchants, suggestedAllowCategories, suggestedBlockCategories, }) => {
|
|
15
|
+
const vendorAllow = (suggestedAllowMerchants ?? []).map((name) => toVendorChipFieldValue(name, false));
|
|
16
|
+
const vendorBlock = (suggestedBlockMerchants ?? []).map((name) => toVendorChipFieldValue(name, false));
|
|
17
|
+
const categoryAllow = (suggestedAllowCategories ?? []).map((category) => toMccCategoryChipFieldValue(category, false));
|
|
18
|
+
const categoryBlock = (suggestedBlockCategories ?? []).map((category) => toMccCategoryChipFieldValue(category, false));
|
|
19
|
+
const vendorPolicy = vendorAllow.length === 0 && vendorBlock.length === 0
|
|
20
|
+
? EMPTY_ALLOW_BLOCK
|
|
21
|
+
: { mode: 'allow', allowItems: vendorAllow, blockItems: vendorBlock };
|
|
22
|
+
const categoryPolicy = categoryAllow.length === 0 && categoryBlock.length === 0
|
|
23
|
+
? EMPTY_ALLOW_BLOCK
|
|
24
|
+
: { mode: 'allow', allowItems: categoryAllow, blockItems: categoryBlock };
|
|
25
|
+
return {
|
|
26
|
+
name: '',
|
|
27
|
+
description: '',
|
|
28
|
+
mode: 'strict',
|
|
29
|
+
vendorPolicy,
|
|
30
|
+
categoryPolicy,
|
|
31
|
+
cardSelections: cardsMasterList.map((row) => ({ ...row, isChecked: false })),
|
|
32
|
+
limits: buildEmptyLimitRows(),
|
|
33
|
+
};
|
|
34
|
+
};
|
package/lib/esm/view/spendManagement/chargeCards/cardPolicy/createCardPolicy/cardPolicyChipIds.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized chip-id derivation so seeds, search-dropdown rows,
|
|
3
|
+
* saved-template recoveries, and the dedupe logic in
|
|
4
|
+
* `AllowBlockChipPicker.handleAddItem` all key off the **same** id for
|
|
5
|
+
* the same logical entity.
|
|
6
|
+
*
|
|
7
|
+
* The dedupe path is content-blind (`existing.id === option.id`): if
|
|
8
|
+
* the id strings don't match, the picker treats the option as a new
|
|
9
|
+
* chip and appends a duplicate. Every producer of chip values for the
|
|
10
|
+
* card-policy flow MUST funnel through these helpers.
|
|
11
|
+
*
|
|
12
|
+
* - Vendors: `vendor-${name.trim().toLowerCase()}`. Two vendors that
|
|
13
|
+
* share an identical display name collapse into one chip.
|
|
14
|
+
* - MCC categories: `mcc-${name}`. Matches the legacy
|
|
15
|
+
* `toCategoryChipOption` convention so the saved-template loader
|
|
16
|
+
* keeps recovering chips by id.
|
|
17
|
+
*/
|
|
18
|
+
export const VENDOR_CHIP_ID_PREFIX = 'vendor-';
|
|
19
|
+
export const MCC_CHIP_ID_PREFIX = 'mcc-';
|
|
20
|
+
export const buildVendorChipId = (name) => `${VENDOR_CHIP_ID_PREFIX}${name.trim().toLowerCase()}`;
|
|
21
|
+
export const buildMccCategoryChipId = (name) => `${MCC_CHIP_ID_PREFIX}${name}`;
|
|
22
|
+
export const toVendorChipFieldValue = (name, isSelected) => ({
|
|
23
|
+
id: buildVendorChipId(name),
|
|
24
|
+
label: name,
|
|
25
|
+
isSelected,
|
|
26
|
+
});
|
|
27
|
+
export const toMccCategoryChipFieldValue = (category, isSelected) => ({
|
|
28
|
+
id: buildMccCategoryChipId(category.name),
|
|
29
|
+
label: category.name,
|
|
30
|
+
isSelected,
|
|
31
|
+
optionalCode: category.mccCodes[0],
|
|
32
|
+
optionalCodes: [...category.mccCodes],
|
|
33
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable id for the transaction-limit toggle row inside `limits`.
|
|
3
|
+
*/
|
|
4
|
+
export const CARD_POLICY_LIMIT_ROW_ID_TRANSACTION = 'transactionLimit';
|
|
5
|
+
/**
|
|
6
|
+
* Stable id for the require-receipt toggle row inside `limits`.
|
|
7
|
+
*/
|
|
8
|
+
export const CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT = 'requireReceipt';
|
|
9
|
+
/**
|
|
10
|
+
* Adapter so callers can hand the `MccCategory[]` array from
|
|
11
|
+
* `cardPolicyState.mccCategories` (or the suggested seeds) straight to
|
|
12
|
+
* the helpers expecting `MccCategoryLike[]`.
|
|
13
|
+
*/
|
|
14
|
+
export const toMccCategoryLike = (category) => ({
|
|
15
|
+
name: category.name,
|
|
16
|
+
mccCodes: category.mccCodes,
|
|
17
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for building the wire-ready
|
|
3
|
+
* `CreateCardPolicyTemplateRequest` payload from either the manual
|
|
4
|
+
* `CardPolicyCreateFormDraft` or the AI CFO `CardPolicyFormDraft`.
|
|
5
|
+
* Both forms emit the same `vendorPolicy` / `categoryPolicy` /
|
|
6
|
+
* `limits` slices, so the mapping primitives are factored out here.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Project the merchant names (chip `label`) selected on the given
|
|
10
|
+
* allow / block field, but only when the field's `mode` matches
|
|
11
|
+
* `forMode`. The other side's chips are preserved in form state for
|
|
12
|
+
* fast mode-flip but never belong on the wire for the current submit.
|
|
13
|
+
*/
|
|
14
|
+
export const collectMerchantNames = (field, forMode) => {
|
|
15
|
+
if (field.mode !== forMode) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const items = forMode === 'allow' ? field.allowItems : field.blockItems;
|
|
19
|
+
return items.filter((item) => item.isSelected).map((item) => item.label);
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Project the MCC codes selected on the given allow / block field.
|
|
23
|
+
* Chips with `optionalCodes` emit every code; chips lacking the plural
|
|
24
|
+
* fall back to the singular `optionalCode`. Chips with neither are
|
|
25
|
+
* dropped silently.
|
|
26
|
+
*/
|
|
27
|
+
export const collectCategoryCodes = (field, forMode) => {
|
|
28
|
+
if (field.mode !== forMode) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const items = forMode === 'allow' ? field.allowItems : field.blockItems;
|
|
32
|
+
return items
|
|
33
|
+
.filter((item) => item.isSelected)
|
|
34
|
+
.flatMap((item) => {
|
|
35
|
+
if (item.optionalCodes != null && item.optionalCodes.length > 0) {
|
|
36
|
+
return item.optionalCodes;
|
|
37
|
+
}
|
|
38
|
+
return item.optionalCode != null ? [item.optionalCode] : [];
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* `null` when the toggle is off (BE treats `null` as "no threshold");
|
|
43
|
+
* otherwise the row's `amount` (or `0` when unset but the toggle is on).
|
|
44
|
+
*/
|
|
45
|
+
export const amountFromLimitRow = (limits, rowId) => {
|
|
46
|
+
const row = limits.find((entry) => entry.id === rowId);
|
|
47
|
+
if (row == null || !row.isEnabled) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return row.amount ?? 0;
|
|
51
|
+
};
|
|
52
|
+
const REVIEW_GROUP_ID_ALLOWED_VENDORS = 'allowed-vendors';
|
|
53
|
+
const REVIEW_GROUP_ID_BLOCKED_VENDORS = 'blocked-vendors';
|
|
54
|
+
const REVIEW_GROUP_ID_ALLOWED_CATEGORIES = 'allowed-categories';
|
|
55
|
+
const REVIEW_GROUP_ID_RESTRICTED_CATEGORIES = 'restricted-categories';
|
|
56
|
+
const REVIEW_GROUP_ID_APPLIED_TO_CARDS = 'applied-to-cards';
|
|
57
|
+
const REVIEW_GROUP_ID_LIMITS = 'limits';
|
|
58
|
+
const REVIEW_LIMIT_CHIP_ID_TRANSACTION = 'limit-transaction';
|
|
59
|
+
const REVIEW_LIMIT_CHIP_ID_RECEIPT = 'limit-receipt';
|
|
60
|
+
const isChipOn = (chip) => chip.isSelected !== false;
|
|
61
|
+
const findGroup = (row, groupId) => row.chipGroups.find((group) => group.id === groupId);
|
|
62
|
+
const projectMerchantNamesFromGroup = (group) => group == null ? [] : group.chips.filter(isChipOn).map((chip) => chip.label);
|
|
63
|
+
const projectCategoryCodesFromGroup = (group) => {
|
|
64
|
+
if (group == null) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const codes = [];
|
|
68
|
+
for (const chip of group.chips) {
|
|
69
|
+
if (!isChipOn(chip)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (chip.optionalCode != null && chip.optionalCode.length > 0) {
|
|
73
|
+
codes.push(chip.optionalCode);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return codes;
|
|
77
|
+
};
|
|
78
|
+
const projectApplyToCardsFromGroup = (group) => {
|
|
79
|
+
if (group == null) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const cardIds = [];
|
|
83
|
+
for (const chip of group.chips) {
|
|
84
|
+
if (!isChipOn(chip)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (chip.cardIds != null && chip.cardIds.length > 0) {
|
|
88
|
+
cardIds.push(...chip.cardIds);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
cardIds.push(chip.id);
|
|
92
|
+
}
|
|
93
|
+
return cardIds;
|
|
94
|
+
};
|
|
95
|
+
const amountFromLimitsChip = (chip) => {
|
|
96
|
+
if (chip == null || chip.isSelected !== true) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (chip.optionalCode == null || chip.optionalCode.length === 0) {
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
const parsed = Number(chip.optionalCode);
|
|
103
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Project a single `auto`-mode review row's chip groups into the wire
|
|
107
|
+
* shape. Used by the AI CFO wire builder when `policyKind === "auto"`,
|
|
108
|
+
* where the source slices are empty and the row's `chipGroups` are
|
|
109
|
+
* canonical.
|
|
110
|
+
*/
|
|
111
|
+
export const projectAutoReviewRowToCardPolicyRequestParts = (row) => {
|
|
112
|
+
const limitsGroup = findGroup(row, REVIEW_GROUP_ID_LIMITS);
|
|
113
|
+
const transactionChip = limitsGroup?.chips.find((chip) => chip.id === REVIEW_LIMIT_CHIP_ID_TRANSACTION);
|
|
114
|
+
const receiptChip = limitsGroup?.chips.find((chip) => chip.id === REVIEW_LIMIT_CHIP_ID_RECEIPT);
|
|
115
|
+
return {
|
|
116
|
+
allowedEntity: {
|
|
117
|
+
merchantNames: projectMerchantNamesFromGroup(findGroup(row, REVIEW_GROUP_ID_ALLOWED_VENDORS)),
|
|
118
|
+
categoryCodes: projectCategoryCodesFromGroup(findGroup(row, REVIEW_GROUP_ID_ALLOWED_CATEGORIES)),
|
|
119
|
+
},
|
|
120
|
+
blockedEntity: {
|
|
121
|
+
merchantNames: projectMerchantNamesFromGroup(findGroup(row, REVIEW_GROUP_ID_BLOCKED_VENDORS)),
|
|
122
|
+
categoryCodes: projectCategoryCodesFromGroup(findGroup(row, REVIEW_GROUP_ID_RESTRICTED_CATEGORIES)),
|
|
123
|
+
},
|
|
124
|
+
applyToCards: projectApplyToCardsFromGroup(findGroup(row, REVIEW_GROUP_ID_APPLIED_TO_CARDS)),
|
|
125
|
+
spendLimits: { transaction: amountFromLimitsChip(transactionChip) },
|
|
126
|
+
requiredReceiptThreshold: amountFromLimitsChip(receiptChip),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Group-id constants used by both auto and bulk wire builders. Exported
|
|
131
|
+
* so the bulk builder can mirror the contract without re-declaring.
|
|
132
|
+
*/
|
|
133
|
+
export const REVIEW_GROUP_IDS = {
|
|
134
|
+
allowedVendors: REVIEW_GROUP_ID_ALLOWED_VENDORS,
|
|
135
|
+
blockedVendors: REVIEW_GROUP_ID_BLOCKED_VENDORS,
|
|
136
|
+
allowedCategories: REVIEW_GROUP_ID_ALLOWED_CATEGORIES,
|
|
137
|
+
restrictedCategories: REVIEW_GROUP_ID_RESTRICTED_CATEGORIES,
|
|
138
|
+
appliedToCards: REVIEW_GROUP_ID_APPLIED_TO_CARDS,
|
|
139
|
+
limits: REVIEW_GROUP_ID_LIMITS,
|
|
140
|
+
};
|
|
141
|
+
export const REVIEW_LIMIT_CHIP_IDS = {
|
|
142
|
+
transaction: REVIEW_LIMIT_CHIP_ID_TRANSACTION,
|
|
143
|
+
receipt: REVIEW_LIMIT_CHIP_ID_RECEIPT,
|
|
144
|
+
};
|