@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
package/lib/esm/view/spendManagement/chargeCards/issueChargeCard/seedAiCardCreationFormDraftEpic.js
DELETED
|
@@ -1,65 +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 { fetchUserListByType } from '../../../userListView/userListViewReducer';
|
|
6
|
-
import { getUserList } from '../../../userListView/userListViewSelector';
|
|
7
|
-
import { clearAiCardCreationFormDraft, issueChargeCardSuccessOrFailure, seedAiCardCreationFormDraft, } from './issueChargeCardReducer';
|
|
8
|
-
const readAllUsersAsUserArray = (state) => getUserList(state.userState, state.userRoleState, state.userListViewState, 'cardHolderCandidate')
|
|
9
|
-
.users.map((person) => person.user)
|
|
10
|
-
.filter((user) => user != null);
|
|
11
|
-
const readCardsCreationPayload = (state, chatSessionId, questionAnswerId) => {
|
|
12
|
-
const qa = getQuestionAnswerByIdForChatSession(state.aiCfoState, chatSessionId, questionAnswerId);
|
|
13
|
-
const visualization = qa?.responses.answer?.content.visualization;
|
|
14
|
-
if (visualization?.type !== 'interactive_form') {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
const data = visualization.data;
|
|
18
|
-
if (!('formType' in data) || data.formType !== 'cards_creation') {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
return data.payload;
|
|
22
|
-
};
|
|
23
|
-
// Pusher (`upsertAnswerPayload`) targets either an explicit
|
|
24
|
-
// `questionAnswerId` or "latest QA in session"; REST
|
|
25
|
-
// (`upsertOrAddQuestionAnswerPayload`) always carries the id. Resolve to
|
|
26
|
-
// the QA the reducer just wrote so we can read the freshly-decoded
|
|
27
|
-
// visualization off state.
|
|
28
|
-
const resolveQuestionAnswerId = (state, action) => {
|
|
29
|
-
if (action.payload.questionAnswerId != null) {
|
|
30
|
-
return action.payload.questionAnswerId;
|
|
31
|
-
}
|
|
32
|
-
const session = state.aiCfoState.aiCfoByChatSessionId[action.payload.chatSessionId];
|
|
33
|
-
return session?.questionAnswers.slice(-1)[0]?.questionAnswerId;
|
|
34
|
-
};
|
|
35
|
-
const isSeedTrigger = (action) => upsertAnswerPayload.match(action) ||
|
|
36
|
-
upsertOrAddQuestionAnswerPayload.match(action);
|
|
37
|
-
export const seedAiCardCreationFormDraftEpic = (actions$, state$) => actions$.pipe(mergeMap((action) => {
|
|
38
|
-
if (issueChargeCardSuccessOrFailure.match(action) &&
|
|
39
|
-
action.payload.fetchState === 'Completed') {
|
|
40
|
-
return of(clearAiCardCreationFormDraft());
|
|
41
|
-
}
|
|
42
|
-
if (!isSeedTrigger(action)) {
|
|
43
|
-
return EMPTY;
|
|
44
|
-
}
|
|
45
|
-
const state = state$.value;
|
|
46
|
-
const questionAnswerId = resolveQuestionAnswerId(state, action);
|
|
47
|
-
if (questionAnswerId == null) {
|
|
48
|
-
return EMPTY;
|
|
49
|
-
}
|
|
50
|
-
const payload = readCardsCreationPayload(state, action.payload.chatSessionId, questionAnswerId);
|
|
51
|
-
if (payload == null) {
|
|
52
|
-
return EMPTY;
|
|
53
|
-
}
|
|
54
|
-
const allUsersNow = readAllUsersAsUserArray(state);
|
|
55
|
-
if (allUsersNow.length > 0) {
|
|
56
|
-
return of(seedAiCardCreationFormDraft(allUsersNow, payload));
|
|
57
|
-
}
|
|
58
|
-
const seedWhenUsersLoaded$ = state$.pipe(map(readAllUsersAsUserArray), distinctUntilChanged((prev, next) => prev.length === next.length &&
|
|
59
|
-
prev.every((user, idx) => user === next[idx])), filter((users) => users.length > 0), take(1), map((users) => seedAiCardCreationFormDraft(users, payload)));
|
|
60
|
-
const userListFetchState = state.userListViewState.userListByType.cardHolderCandidate.fetchState;
|
|
61
|
-
if (userListFetchState === 'In-Progress') {
|
|
62
|
-
return seedWhenUsersLoaded$;
|
|
63
|
-
}
|
|
64
|
-
return concat(of(fetchUserListByType('cardHolderCandidate', false)), seedWhenUsersLoaded$);
|
|
65
|
-
}));
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default credit limit frequency emitted on the wire when the AI CFO
|
|
3
|
-
* card creation form is submitted. The interactive form has no
|
|
4
|
-
* frequency field today (v1 product decision: AI CFO only ever issues
|
|
5
|
-
* monthly virtual credit cards).
|
|
6
|
-
*/
|
|
7
|
-
const DEFAULT_FREQUENCY = 'monthly';
|
|
8
|
-
const DEFAULT_CURRENCY_CODE = 'USD';
|
|
9
|
-
const DEFAULT_CURRENCY_SYMBOL = '$';
|
|
10
|
-
/**
|
|
11
|
-
* Map a single AI card-creation row to the `IssueChargeCardLocalData`
|
|
12
|
-
* shape consumed by `issueChargeCardEpic`.
|
|
13
|
-
*
|
|
14
|
-
* AI CFO only issues `business_virtual_credit_card`s in v1, so:
|
|
15
|
-
* - `chargeCardType` is hard-coded to `'credit_card'`
|
|
16
|
-
* - `creditCard.cardType` is hard-coded to `'business_virtual_credit_card'`
|
|
17
|
-
* - the physicalCard / debitCard.* slots are populated with empty
|
|
18
|
-
* defaults that the epic ignores.
|
|
19
|
-
*/
|
|
20
|
-
const mapCardRow = (row) => {
|
|
21
|
-
const ownerUserId = row.owner.selectedUser?.userId;
|
|
22
|
-
return {
|
|
23
|
-
lineId: row.id,
|
|
24
|
-
chargeCardType: 'credit_card',
|
|
25
|
-
creditCard: {
|
|
26
|
-
cardType: 'business_virtual_credit_card',
|
|
27
|
-
virtualCard: {
|
|
28
|
-
cardName: row.label,
|
|
29
|
-
frequency: DEFAULT_FREQUENCY,
|
|
30
|
-
userIds: ownerUserId != null ? [ownerUserId] : [],
|
|
31
|
-
amount: {
|
|
32
|
-
amount: row.recommendedLimit ?? 0,
|
|
33
|
-
currencyCode: DEFAULT_CURRENCY_CODE,
|
|
34
|
-
currencySymbol: DEFAULT_CURRENCY_SYMBOL,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
physicalCard: {
|
|
38
|
-
cardName: '',
|
|
39
|
-
frequency: DEFAULT_FREQUENCY,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
debitCard: {
|
|
43
|
-
cardType: 'business_virtual_debit_card',
|
|
44
|
-
virtualCard: {
|
|
45
|
-
cardName: '',
|
|
46
|
-
frequency: DEFAULT_FREQUENCY,
|
|
47
|
-
userIds: [],
|
|
48
|
-
},
|
|
49
|
-
physicalCard: {
|
|
50
|
-
cardName: '',
|
|
51
|
-
frequency: DEFAULT_FREQUENCY,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* Convert a `CardsFormDraft` into the `IssueChargeCardLocalData[]`
|
|
58
|
-
* payload `issueChargeCardEpic` expects in
|
|
59
|
-
* `state.issueChargeCardState.localData`.
|
|
60
|
-
*
|
|
61
|
-
* Returns an empty array when `cardRows` is empty so callers can
|
|
62
|
-
* short-circuit without dispatching a no-op bulk POST.
|
|
63
|
-
*/
|
|
64
|
-
export const toIssueChargeCardLocalDataFromDraft = (draft) => draft.cardRows.map(mapCardRow);
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { AiCardPolicyFormDraftSeedContext, CardPolicyCardSelectionRow, CardPolicyFormDraft, CardPolicyReviewRow } from './cardPolicyFormDraftTypes';
|
|
2
|
-
export interface ApplyAiCardPolicyFormDraftUpdateArgs {
|
|
3
|
-
context: AiCardPolicyFormDraftSeedContext | undefined;
|
|
4
|
-
next: CardPolicyFormDraft;
|
|
5
|
-
prev: CardPolicyFormDraft | undefined;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Single source of truth for "what the next AI card-policy form draft
|
|
9
|
-
* should look like after a `watchHandler` flush".
|
|
10
|
-
*
|
|
11
|
-
* Branches on `next.policyKind`:
|
|
12
|
-
*
|
|
13
|
-
* - `auto`: pass-through. Chip groups on `policiesReview` are the
|
|
14
|
-
* canonical source of truth; the source slices stay empty.
|
|
15
|
-
*
|
|
16
|
-
* - `guided`: mirrors review-step chip edits (diffed against
|
|
17
|
-
* `prev.policiesReview`) back into the source slices
|
|
18
|
-
* (`vendorPolicy` / `categoryPolicy` / `cardSelections` / `limits`),
|
|
19
|
-
* then re-derives `policiesReview` from the bridged source slices
|
|
20
|
-
* via `deriveAiPolicyReviewRowsFromInputs` so the review always
|
|
21
|
-
* reflects the freshest live state. Replaces the
|
|
22
|
-
* `useReviewChipEditBridge` hook that previously lived in
|
|
23
|
-
* `CardPolicyInteractiveForm`.
|
|
24
|
-
*
|
|
25
|
-
* - `upload`: passes through the user's chip edits on
|
|
26
|
-
* `policiesReview` (the wire reads from those chip groups). The
|
|
27
|
-
* "Applied to Cards" chip group is patched in-place from
|
|
28
|
-
* `cardSelections` (the user's Step 2 picks).
|
|
29
|
-
*/
|
|
30
|
-
export declare const applyAiCardPolicyFormDraftUpdate: ({ next, prev, context, }: ApplyAiCardPolicyFormDraftUpdateArgs) => CardPolicyFormDraft;
|
|
31
|
-
/**
|
|
32
|
-
* Replace (or insert) the "Applied to Cards" chip group on every
|
|
33
|
-
* upload review row with avatar chips derived from the current
|
|
34
|
-
* `cardSelections`. The new group is inserted before the "Limits"
|
|
35
|
-
* group so the visual order stays Vendors → Categories → Cards →
|
|
36
|
-
* Limits.
|
|
37
|
-
*/
|
|
38
|
-
export declare const patchUploadReviewWithAppliedCards: (reviewRows: CardPolicyReviewRow[], cardSelections: CardPolicyCardSelectionRow[]) => CardPolicyReviewRow[];
|
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.patchUploadReviewWithAppliedCards = exports.applyAiCardPolicyFormDraftUpdate = void 0;
|
|
4
|
-
const deriveAiPolicyReviewRowsFromInputs_1 = require("./deriveAiPolicyReviewRowsFromInputs");
|
|
5
|
-
/**
|
|
6
|
-
* Single source of truth for "what the next AI card-policy form draft
|
|
7
|
-
* should look like after a `watchHandler` flush".
|
|
8
|
-
*
|
|
9
|
-
* Branches on `next.policyKind`:
|
|
10
|
-
*
|
|
11
|
-
* - `auto`: pass-through. Chip groups on `policiesReview` are the
|
|
12
|
-
* canonical source of truth; the source slices stay empty.
|
|
13
|
-
*
|
|
14
|
-
* - `guided`: mirrors review-step chip edits (diffed against
|
|
15
|
-
* `prev.policiesReview`) back into the source slices
|
|
16
|
-
* (`vendorPolicy` / `categoryPolicy` / `cardSelections` / `limits`),
|
|
17
|
-
* then re-derives `policiesReview` from the bridged source slices
|
|
18
|
-
* via `deriveAiPolicyReviewRowsFromInputs` so the review always
|
|
19
|
-
* reflects the freshest live state. Replaces the
|
|
20
|
-
* `useReviewChipEditBridge` hook that previously lived in
|
|
21
|
-
* `CardPolicyInteractiveForm`.
|
|
22
|
-
*
|
|
23
|
-
* - `upload`: passes through the user's chip edits on
|
|
24
|
-
* `policiesReview` (the wire reads from those chip groups). The
|
|
25
|
-
* "Applied to Cards" chip group is patched in-place from
|
|
26
|
-
* `cardSelections` (the user's Step 2 picks).
|
|
27
|
-
*/
|
|
28
|
-
const applyAiCardPolicyFormDraftUpdate = ({ next, prev, context, }) => {
|
|
29
|
-
if (next.policyKind === 'auto') {
|
|
30
|
-
return next;
|
|
31
|
-
}
|
|
32
|
-
if (next.policyKind === 'upload') {
|
|
33
|
-
return applyUploadUpdate({ next, prev });
|
|
34
|
-
}
|
|
35
|
-
return applyGuidedUpdate({ next, prev, context });
|
|
36
|
-
};
|
|
37
|
-
exports.applyAiCardPolicyFormDraftUpdate = applyAiCardPolicyFormDraftUpdate;
|
|
38
|
-
const applyGuidedUpdate = ({ next, prev, context, }) => {
|
|
39
|
-
const bridged = bridgeGuidedPoliciesReviewToSources({ next, prev });
|
|
40
|
-
if (context == null) {
|
|
41
|
-
return bridged;
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
...bridged,
|
|
45
|
-
policiesReview: (0, deriveAiPolicyReviewRowsFromInputs_1.deriveAiPolicyReviewRowsFromInputs)({
|
|
46
|
-
vendorPolicy: bridged.vendorPolicy,
|
|
47
|
-
categoryPolicy: bridged.categoryPolicy,
|
|
48
|
-
cardSelections: bridged.cardSelections,
|
|
49
|
-
limits: bridged.limits,
|
|
50
|
-
suggestedPoliciesSeed: context.suggestedPoliciesSeed,
|
|
51
|
-
prevPoliciesReview: bridged.policiesReview,
|
|
52
|
-
}),
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
const APPLIED_TO_CARDS_GROUP_ID = 'applied-to-cards';
|
|
56
|
-
const LIMITS_GROUP_ID = 'limits';
|
|
57
|
-
const applyUploadUpdate = ({ next, prev, }) => {
|
|
58
|
-
// Mirror review-side avatar / limits edits into source slices so the
|
|
59
|
-
// bulk-submit builder (which reads `cardSelections` for upload's
|
|
60
|
-
// applyToCards) sees the user's latest intent.
|
|
61
|
-
const bridged = bridgeUploadPoliciesReviewToSources({ next, prev });
|
|
62
|
-
if (!haveCardSelectionsChanged(prev?.cardSelections, bridged.cardSelections)) {
|
|
63
|
-
return bridged;
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
...bridged,
|
|
67
|
-
policiesReview: (0, exports.patchUploadReviewWithAppliedCards)(bridged.policiesReview, bridged.cardSelections),
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
const haveCardSelectionsChanged = (prevSelections, nextSelections) => {
|
|
71
|
-
const prevCheckedIds = (prevSelections ?? [])
|
|
72
|
-
.filter((row) => row.isChecked)
|
|
73
|
-
.map((row) => row.id);
|
|
74
|
-
const nextCheckedIds = nextSelections
|
|
75
|
-
.filter((row) => row.isChecked)
|
|
76
|
-
.map((row) => row.id);
|
|
77
|
-
if (prevCheckedIds.length !== nextCheckedIds.length) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return prevCheckedIds.some((id, idx) => id !== nextCheckedIds[idx]);
|
|
81
|
-
};
|
|
82
|
-
const buildAppliedOwnerSummaries = (cardSelections) => {
|
|
83
|
-
const summaries = new Map();
|
|
84
|
-
for (const card of cardSelections) {
|
|
85
|
-
if (!card.isChecked) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const existing = summaries.get(card.owner.userId);
|
|
89
|
-
if (existing != null) {
|
|
90
|
-
existing.cardIds.push(card.id);
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
summaries.set(card.owner.userId, {
|
|
94
|
-
userId: card.owner.userId,
|
|
95
|
-
displayName: card.owner.displayName,
|
|
96
|
-
cardIds: [card.id],
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
return Array.from(summaries.values());
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Replace (or insert) the "Applied to Cards" chip group on every
|
|
103
|
-
* upload review row with avatar chips derived from the current
|
|
104
|
-
* `cardSelections`. The new group is inserted before the "Limits"
|
|
105
|
-
* group so the visual order stays Vendors → Categories → Cards →
|
|
106
|
-
* Limits.
|
|
107
|
-
*/
|
|
108
|
-
const patchUploadReviewWithAppliedCards = (reviewRows, cardSelections) => {
|
|
109
|
-
const owners = buildAppliedOwnerSummaries(cardSelections);
|
|
110
|
-
const cardChips = owners.map((owner) => ({
|
|
111
|
-
id: owner.userId,
|
|
112
|
-
label: owner.displayName,
|
|
113
|
-
chipKind: 'avatar',
|
|
114
|
-
cardIds: owner.cardIds,
|
|
115
|
-
isSelected: true,
|
|
116
|
-
}));
|
|
117
|
-
const cardsGroup = cardChips.length > 0
|
|
118
|
-
? {
|
|
119
|
-
id: APPLIED_TO_CARDS_GROUP_ID,
|
|
120
|
-
label: 'Applied to Cards',
|
|
121
|
-
chips: cardChips,
|
|
122
|
-
}
|
|
123
|
-
: null;
|
|
124
|
-
return reviewRows.map((row) => {
|
|
125
|
-
const withoutCards = row.chipGroups.filter((group) => group.id !== APPLIED_TO_CARDS_GROUP_ID);
|
|
126
|
-
if (cardsGroup == null) {
|
|
127
|
-
return { ...row, chipGroups: withoutCards };
|
|
128
|
-
}
|
|
129
|
-
const limitsIdx = withoutCards.findIndex((group) => group.id === LIMITS_GROUP_ID);
|
|
130
|
-
if (limitsIdx === -1) {
|
|
131
|
-
return { ...row, chipGroups: [...withoutCards, cardsGroup] };
|
|
132
|
-
}
|
|
133
|
-
const before = withoutCards.slice(0, limitsIdx);
|
|
134
|
-
const after = withoutCards.slice(limitsIdx);
|
|
135
|
-
return { ...row, chipGroups: [...before, cardsGroup, ...after] };
|
|
136
|
-
});
|
|
137
|
-
};
|
|
138
|
-
exports.patchUploadReviewWithAppliedCards = patchUploadReviewWithAppliedCards;
|
|
139
|
-
/**
|
|
140
|
-
* Mirror diffs in `next.policiesReview` chips back into the source
|
|
141
|
-
* slices for guided mode. Detects per-chip `isSelected` and
|
|
142
|
-
* `optionalCode` changes vs `prev` and writes them to:
|
|
143
|
-
* - `limits` for `kind: "limits"` group chips,
|
|
144
|
-
* - `cardSelections` for avatar chips, and
|
|
145
|
-
* - `vendorPolicy` / `categoryPolicy` for positive/negative chips.
|
|
146
|
-
*/
|
|
147
|
-
const bridgeGuidedPoliciesReviewToSources = ({ next, prev, }) => {
|
|
148
|
-
if (prev == null || prev.policiesReview.length === 0) {
|
|
149
|
-
return next;
|
|
150
|
-
}
|
|
151
|
-
const reviewDiffs = collectReviewChipDiffs(prev.policiesReview, next.policiesReview);
|
|
152
|
-
if (reviewDiffs.length === 0) {
|
|
153
|
-
return next;
|
|
154
|
-
}
|
|
155
|
-
let limits = next.limits;
|
|
156
|
-
let cardSelections = next.cardSelections;
|
|
157
|
-
let vendorPolicy = next.vendorPolicy;
|
|
158
|
-
let categoryPolicy = next.categoryPolicy;
|
|
159
|
-
for (const diff of reviewDiffs) {
|
|
160
|
-
if (diff.groupKind === 'limits' || diff.chip.id.startsWith('limit-')) {
|
|
161
|
-
limits = applyLimitChipDiffToLimits(limits, diff);
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (diff.chip.chipKind === 'avatar') {
|
|
165
|
-
cardSelections = applyAvatarChipDiffToCardSelections(cardSelections, diff);
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (diff.changedFields.has('isSelected')) {
|
|
169
|
-
const writtenToVendor = tryWriteAllowBlock(vendorPolicy, diff);
|
|
170
|
-
if (writtenToVendor != null) {
|
|
171
|
-
vendorPolicy = writtenToVendor;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
const writtenToCategory = tryWriteAllowBlock(categoryPolicy, diff);
|
|
175
|
-
if (writtenToCategory != null) {
|
|
176
|
-
categoryPolicy = writtenToCategory;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return {
|
|
181
|
-
...next,
|
|
182
|
-
limits,
|
|
183
|
-
cardSelections,
|
|
184
|
-
vendorPolicy,
|
|
185
|
-
categoryPolicy,
|
|
186
|
-
};
|
|
187
|
-
};
|
|
188
|
-
/**
|
|
189
|
-
* Upload-mode bridge — narrower than guided. Vendor / category chips in
|
|
190
|
-
* the review are the wire's source of truth (no source slice to write
|
|
191
|
-
* back to); avatar chips mirror into `cardSelections` (the wire's
|
|
192
|
-
* `applyToCards` projection for upload reads from there); limit chips
|
|
193
|
-
* are read from the review chip group directly by the wire, so no
|
|
194
|
-
* mirror is needed.
|
|
195
|
-
*/
|
|
196
|
-
const bridgeUploadPoliciesReviewToSources = ({ next, prev, }) => {
|
|
197
|
-
if (prev == null || prev.policiesReview.length === 0) {
|
|
198
|
-
return next;
|
|
199
|
-
}
|
|
200
|
-
const reviewDiffs = collectReviewChipDiffs(prev.policiesReview, next.policiesReview);
|
|
201
|
-
if (reviewDiffs.length === 0) {
|
|
202
|
-
return next;
|
|
203
|
-
}
|
|
204
|
-
let cardSelections = next.cardSelections;
|
|
205
|
-
for (const diff of reviewDiffs) {
|
|
206
|
-
if (diff.chip.chipKind === 'avatar') {
|
|
207
|
-
cardSelections = applyAvatarChipDiffToCardSelections(cardSelections, diff);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (cardSelections === next.cardSelections) {
|
|
211
|
-
return next;
|
|
212
|
-
}
|
|
213
|
-
return { ...next, cardSelections };
|
|
214
|
-
};
|
|
215
|
-
const collectReviewChipDiffs = (prevRows, nextRows) => {
|
|
216
|
-
const prevById = new Map(prevRows.map((row) => [row.id, row]));
|
|
217
|
-
const diffs = [];
|
|
218
|
-
for (const row of nextRows) {
|
|
219
|
-
const prevRow = prevById.get(row.id);
|
|
220
|
-
if (prevRow == null) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const prevGroupsById = new Map(prevRow.chipGroups.map((group) => [group.id, group]));
|
|
224
|
-
for (const group of row.chipGroups) {
|
|
225
|
-
const prevGroup = prevGroupsById.get(group.id);
|
|
226
|
-
if (prevGroup == null) {
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
const prevChipsById = new Map(prevGroup.chips.map((chip) => [chip.id, chip]));
|
|
230
|
-
for (const chip of group.chips) {
|
|
231
|
-
const prevChip = prevChipsById.get(chip.id);
|
|
232
|
-
if (prevChip == null) {
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
const changed = new Set();
|
|
236
|
-
if (chip.isSelected !== prevChip.isSelected) {
|
|
237
|
-
changed.add('isSelected');
|
|
238
|
-
}
|
|
239
|
-
if (chip.optionalCode !== prevChip.optionalCode) {
|
|
240
|
-
changed.add('optionalCode');
|
|
241
|
-
}
|
|
242
|
-
if (changed.size > 0) {
|
|
243
|
-
diffs.push({ chip, changedFields: changed, groupKind: group.kind });
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return diffs;
|
|
249
|
-
};
|
|
250
|
-
const REVIEW_LIMIT_CHIP_ID_TO_LIMIT_ROW_ID = {
|
|
251
|
-
'limit-transaction': 'transactionLimit',
|
|
252
|
-
'limit-receipt': 'requireReceipt',
|
|
253
|
-
};
|
|
254
|
-
const applyLimitChipDiffToLimits = (limits, diff) => {
|
|
255
|
-
const mapped = REVIEW_LIMIT_CHIP_ID_TO_LIMIT_ROW_ID[diff.chip.id];
|
|
256
|
-
const directMatch = limits.find((row) => row.id === diff.chip.id) != null;
|
|
257
|
-
const rowId = mapped ?? (directMatch ? diff.chip.id : undefined);
|
|
258
|
-
if (rowId == null) {
|
|
259
|
-
return limits;
|
|
260
|
-
}
|
|
261
|
-
const idx = limits.findIndex((row) => row.id === rowId);
|
|
262
|
-
if (idx === -1) {
|
|
263
|
-
return limits;
|
|
264
|
-
}
|
|
265
|
-
let nextRow = limits[idx];
|
|
266
|
-
if (diff.changedFields.has('isSelected')) {
|
|
267
|
-
nextRow = { ...nextRow, isEnabled: diff.chip.isSelected === true };
|
|
268
|
-
}
|
|
269
|
-
if (diff.changedFields.has('optionalCode')) {
|
|
270
|
-
const raw = diff.chip.optionalCode;
|
|
271
|
-
const numericNext = raw != null && raw !== '' ? Number(raw) : undefined;
|
|
272
|
-
nextRow = { ...nextRow, amount: numericNext };
|
|
273
|
-
}
|
|
274
|
-
if (nextRow === limits[idx]) {
|
|
275
|
-
return limits;
|
|
276
|
-
}
|
|
277
|
-
const copy = limits.slice();
|
|
278
|
-
copy[idx] = nextRow;
|
|
279
|
-
return copy;
|
|
280
|
-
};
|
|
281
|
-
const applyAvatarChipDiffToCardSelections = (cardSelections, diff) => {
|
|
282
|
-
if (!diff.changedFields.has('isSelected')) {
|
|
283
|
-
return cardSelections;
|
|
284
|
-
}
|
|
285
|
-
const targetIds = diff.chip.cardIds != null && diff.chip.cardIds.length > 0
|
|
286
|
-
? diff.chip.cardIds
|
|
287
|
-
: [diff.chip.id];
|
|
288
|
-
const targetSet = new Set(targetIds);
|
|
289
|
-
let mutated = false;
|
|
290
|
-
const next = cardSelections.map((row) => {
|
|
291
|
-
if (!targetSet.has(row.id)) {
|
|
292
|
-
return row;
|
|
293
|
-
}
|
|
294
|
-
const desired = diff.chip.isSelected === true;
|
|
295
|
-
if (row.isChecked === desired) {
|
|
296
|
-
return row;
|
|
297
|
-
}
|
|
298
|
-
mutated = true;
|
|
299
|
-
return { ...row, isChecked: desired };
|
|
300
|
-
});
|
|
301
|
-
return mutated ? next : cardSelections;
|
|
302
|
-
};
|
|
303
|
-
const tryWriteAllowBlock = (field, diff) => {
|
|
304
|
-
const desired = diff.chip.isSelected === true;
|
|
305
|
-
const written = tryFlipItemInSide(field.allowItems, diff, desired);
|
|
306
|
-
if (written != null) {
|
|
307
|
-
return { ...field, allowItems: written };
|
|
308
|
-
}
|
|
309
|
-
const writtenBlock = tryFlipItemInSide(field.blockItems, diff, desired);
|
|
310
|
-
if (writtenBlock != null) {
|
|
311
|
-
return { ...field, blockItems: writtenBlock };
|
|
312
|
-
}
|
|
313
|
-
return null;
|
|
314
|
-
};
|
|
315
|
-
const tryFlipItemInSide = (items, diff, desired) => {
|
|
316
|
-
let idx = items.findIndex((item) => item.id === diff.chip.id);
|
|
317
|
-
if (idx === -1 && diff.chip.optionalCode != null) {
|
|
318
|
-
// Auto / upload mode category chips use the raw MCC code as
|
|
319
|
-
// `chip.id` while the source items use `mcc-{name}`; fall back to
|
|
320
|
-
// matching by code so the bridge round-trips both shapes.
|
|
321
|
-
idx = items.findIndex((item) => item.optionalCode === diff.chip.optionalCode ||
|
|
322
|
-
(item.optionalCodes ?? []).includes(diff.chip.optionalCode ?? ''));
|
|
323
|
-
}
|
|
324
|
-
if (idx === -1) {
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
if (items[idx].isSelected === desired) {
|
|
328
|
-
return items;
|
|
329
|
-
}
|
|
330
|
-
const copy = items.slice();
|
|
331
|
-
copy[idx] = { ...items[idx], isSelected: desired };
|
|
332
|
-
return copy;
|
|
333
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { CardPolicyChipFieldValue, CardPolicyFormDraft, ExtractedCardPolicyDataView } from './cardPolicyFormDraftTypes';
|
|
2
|
-
export interface ApplyExtractedPolicyToAiCardPolicyDraftArgs {
|
|
3
|
-
/** Category universe the chip picker can offer (search dropdown contents). */
|
|
4
|
-
categorySearchOptions: CardPolicyChipFieldValue[];
|
|
5
|
-
/** Current draft — preserved for slices the response does not cover. */
|
|
6
|
-
current: CardPolicyFormDraft;
|
|
7
|
-
/** The extracted rules returned by the cards micro-service. */
|
|
8
|
-
extracted: ExtractedCardPolicyDataView;
|
|
9
|
-
/** Vendor universe the chip picker can offer. */
|
|
10
|
-
vendorSearchOptions: CardPolicyChipFieldValue[];
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Apply an extracted policy document response to the AI CFO card-policy
|
|
14
|
-
* draft. Touches only the slices the extract API populates:
|
|
15
|
-
* - `vendorPolicy` from allow/block `merchantNames` (with allow-wins
|
|
16
|
-
* flip).
|
|
17
|
-
* - `categoryPolicy` from allow/block `categoryCodes` (matched on
|
|
18
|
-
* `optionalCode`).
|
|
19
|
-
* - `limits` rows from `transactionLimit` / `requiredReceiptThreshold`.
|
|
20
|
-
*
|
|
21
|
-
* `cardSelections` and `policiesReview` are preserved verbatim — the
|
|
22
|
-
* user still picks cards on Step 2 and the review step is seeded
|
|
23
|
-
* separately.
|
|
24
|
-
*/
|
|
25
|
-
export declare const applyExtractedPolicyToAiCardPolicyDraft: ({ extracted, current, vendorSearchOptions, categorySearchOptions, }: ApplyExtractedPolicyToAiCardPolicyDraftArgs) => CardPolicyFormDraft;
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.applyExtractedPolicyToAiCardPolicyDraft = void 0;
|
|
4
|
-
const cardPolicyChipIds_1 = require("./cardPolicyChipIds");
|
|
5
|
-
const cardPolicyFormDraftTypes_1 = require("./cardPolicyFormDraftTypes");
|
|
6
|
-
/**
|
|
7
|
-
* Apply an extracted policy document response to the AI CFO card-policy
|
|
8
|
-
* draft. Touches only the slices the extract API populates:
|
|
9
|
-
* - `vendorPolicy` from allow/block `merchantNames` (with allow-wins
|
|
10
|
-
* flip).
|
|
11
|
-
* - `categoryPolicy` from allow/block `categoryCodes` (matched on
|
|
12
|
-
* `optionalCode`).
|
|
13
|
-
* - `limits` rows from `transactionLimit` / `requiredReceiptThreshold`.
|
|
14
|
-
*
|
|
15
|
-
* `cardSelections` and `policiesReview` are preserved verbatim — the
|
|
16
|
-
* user still picks cards on Step 2 and the review step is seeded
|
|
17
|
-
* separately.
|
|
18
|
-
*/
|
|
19
|
-
const applyExtractedPolicyToAiCardPolicyDraft = ({ extracted, current, vendorSearchOptions, categorySearchOptions, }) => ({
|
|
20
|
-
...current,
|
|
21
|
-
vendorPolicy: buildAllowBlockField({
|
|
22
|
-
allowList: extracted.allowedEntity.merchantNames,
|
|
23
|
-
blockList: extracted.blockedEntity.merchantNames,
|
|
24
|
-
currentAllowItems: current.vendorPolicy.allowItems,
|
|
25
|
-
currentBlockItems: current.vendorPolicy.blockItems,
|
|
26
|
-
universe: vendorSearchOptions,
|
|
27
|
-
matchOnCode: false,
|
|
28
|
-
}),
|
|
29
|
-
categoryPolicy: buildAllowBlockField({
|
|
30
|
-
allowList: extracted.allowedEntity.categoryCodes,
|
|
31
|
-
blockList: extracted.blockedEntity.categoryCodes,
|
|
32
|
-
currentAllowItems: current.categoryPolicy.allowItems,
|
|
33
|
-
currentBlockItems: current.categoryPolicy.blockItems,
|
|
34
|
-
universe: categorySearchOptions,
|
|
35
|
-
matchOnCode: true,
|
|
36
|
-
}),
|
|
37
|
-
limits: buildLimitRows({
|
|
38
|
-
current: current.limits,
|
|
39
|
-
transactionLimit: extracted.transactionLimit,
|
|
40
|
-
requiredReceiptThreshold: extracted.requiredReceiptThreshold,
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
43
|
-
exports.applyExtractedPolicyToAiCardPolicyDraft = applyExtractedPolicyToAiCardPolicyDraft;
|
|
44
|
-
const buildAllowBlockField = ({ allowList, blockList, currentAllowItems, currentBlockItems, universe, matchOnCode, }) => {
|
|
45
|
-
const allowItems = buildSideItems({
|
|
46
|
-
selectedValues: allowList,
|
|
47
|
-
currentItems: currentAllowItems,
|
|
48
|
-
universe,
|
|
49
|
-
matchOnCode,
|
|
50
|
-
});
|
|
51
|
-
const blockItems = buildSideItems({
|
|
52
|
-
selectedValues: blockList,
|
|
53
|
-
currentItems: currentBlockItems,
|
|
54
|
-
universe,
|
|
55
|
-
matchOnCode,
|
|
56
|
-
});
|
|
57
|
-
const mode = allowList.length === 0 && blockList.length > 0 ? 'block' : 'allow';
|
|
58
|
-
return { mode, allowItems, blockItems };
|
|
59
|
-
};
|
|
60
|
-
const buildSideItems = ({ selectedValues, currentItems, universe, matchOnCode, }) => {
|
|
61
|
-
const selectedKeys = new Set(selectedValues
|
|
62
|
-
.map((value) => normalizeKey(value))
|
|
63
|
-
.filter((key) => key != null));
|
|
64
|
-
const items = [];
|
|
65
|
-
const seenIds = new Set();
|
|
66
|
-
for (const item of currentItems) {
|
|
67
|
-
const key = normalizeKey(matchOnCode ? item.optionalCode : item.label);
|
|
68
|
-
const isSelected = key != null && selectedKeys.has(key);
|
|
69
|
-
items.push({ ...item, isSelected });
|
|
70
|
-
seenIds.add(item.id);
|
|
71
|
-
if (isSelected && key != null) {
|
|
72
|
-
selectedKeys.delete(key);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const option of universe) {
|
|
76
|
-
if (selectedKeys.size === 0) {
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
const key = normalizeKey(matchOnCode ? option.optionalCode : option.label);
|
|
80
|
-
if (key == null || !selectedKeys.has(key) || seenIds.has(option.id)) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
items.push({ ...option, isSelected: true });
|
|
84
|
-
seenIds.add(option.id);
|
|
85
|
-
selectedKeys.delete(key);
|
|
86
|
-
}
|
|
87
|
-
for (const value of selectedValues) {
|
|
88
|
-
const key = normalizeKey(value);
|
|
89
|
-
if (key == null || !selectedKeys.has(key)) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
const id = matchOnCode
|
|
93
|
-
? (0, cardPolicyChipIds_1.buildMccCategoryChipId)(value)
|
|
94
|
-
: (0, cardPolicyChipIds_1.buildVendorChipId)(value);
|
|
95
|
-
if (seenIds.has(id)) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
items.push({
|
|
99
|
-
id,
|
|
100
|
-
label: value,
|
|
101
|
-
isSelected: true,
|
|
102
|
-
...(matchOnCode ? { optionalCode: value } : {}),
|
|
103
|
-
});
|
|
104
|
-
seenIds.add(id);
|
|
105
|
-
selectedKeys.delete(key);
|
|
106
|
-
}
|
|
107
|
-
return items;
|
|
108
|
-
};
|
|
109
|
-
const buildLimitRows = ({ current, transactionLimit, requiredReceiptThreshold, }) => current.map((row) => {
|
|
110
|
-
if (row.id === cardPolicyFormDraftTypes_1.CARD_POLICY_LIMIT_ROW_ID_TRANSACTION) {
|
|
111
|
-
return amountToLimitRow(row, transactionLimit);
|
|
112
|
-
}
|
|
113
|
-
if (row.id === cardPolicyFormDraftTypes_1.CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT) {
|
|
114
|
-
return amountToLimitRow(row, requiredReceiptThreshold);
|
|
115
|
-
}
|
|
116
|
-
return row;
|
|
117
|
-
});
|
|
118
|
-
const amountToLimitRow = (row, amount) => {
|
|
119
|
-
if (amount > 0) {
|
|
120
|
-
return { ...row, isEnabled: true, amount };
|
|
121
|
-
}
|
|
122
|
-
return { ...row, isEnabled: false, amount: row.amount };
|
|
123
|
-
};
|
|
124
|
-
const normalizeKey = (value) => value == null || value.length === 0 ? undefined : value.toLowerCase().trim();
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
import { updatePolicyDocumentExtractionSuccess } from '../../../../../entity/cardPolicy/cardPolicyReducer';
|
|
4
|
-
import { RootState } from '../../../../../reducer';
|
|
5
|
-
import { applyExtractedPolicyToAiCardPolicyDraft, applyExtractedPolicyToManualCardPolicyDraft } from './createCardPolicyReducer';
|
|
6
|
-
export type ActionType = ReturnType<typeof applyExtractedPolicyToAiCardPolicyDraft> | ReturnType<typeof applyExtractedPolicyToManualCardPolicyDraft>;
|
|
7
|
-
type InboundAction = ReturnType<typeof updatePolicyDocumentExtractionSuccess>;
|
|
8
|
-
/**
|
|
9
|
-
* Bridge epic — fans `updatePolicyDocumentExtractionSuccess` out to the
|
|
10
|
-
* right card-policy form draft based on which surface kicked the upload
|
|
11
|
-
* off:
|
|
12
|
-
*
|
|
13
|
-
* - `aiCfoAnswerId != null` → the AI CFO upload-mode wizard owns it;
|
|
14
|
-
* dispatch `applyExtractedPolicyToAiCardPolicyDraft` so the AI draft
|
|
15
|
-
* picks up the extracted allow/block + limits. The wizard's `step_5`
|
|
16
|
-
* review chips are separately patched by
|
|
17
|
-
* `policyRecommendationFromUploadEpic` (the second hop in the chain).
|
|
18
|
-
* - `aiCfoAnswerId == null` → the manual `CardPolicyCreatePage` owns
|
|
19
|
-
* it; dispatch `applyExtractedPolicyToManualCardPolicyDraft` so the
|
|
20
|
-
* manual draft picks up name + allow/block + limits.
|
|
21
|
-
*
|
|
22
|
-
* The bridge reads vendor + category universes off CES
|
|
23
|
-
* (`cardPolicyState.vendorSearch.vendors`,
|
|
24
|
-
* `cardPolicyState.mccCategories`) instead of taking them through the
|
|
25
|
-
* payload, so any caller that drops the action wires up automatically.
|
|
26
|
-
*/
|
|
27
|
-
export declare const applyExtractedPolicyToDraftEpic: (actions$: ActionsObservable<InboundAction>, state$: StateObservable<RootState>) => Observable<ActionType>;
|
|
28
|
-
export {};
|