@zeniai/client-epic-state 5.2.58-beta0ND → 5.2.58-beta1ND
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/esm/view/recommendation/recommendationPayload.js +3 -1
- package/lib/esm/view/recommendation/recommendationReducer.js +9 -7
- package/lib/esm/view/recommendation/recommendationSelector.js +2 -2
- package/lib/esm/view/recommendation/recommendationState.js +0 -1
- package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +15 -10
- package/lib/view/recommendation/recommendationPayload.d.ts +1 -0
- package/lib/view/recommendation/recommendationPayload.js +5 -0
- package/lib/view/recommendation/recommendationReducer.d.ts +1 -3
- package/lib/view/recommendation/recommendationReducer.js +8 -6
- package/lib/view/recommendation/recommendationSelector.d.ts +1 -1
- package/lib/view/recommendation/recommendationSelector.js +1 -1
- package/lib/view/recommendation/recommendationState.d.ts +1 -3
- package/lib/view/recommendation/recommendationState.js +1 -3
- package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +14 -9
- package/package.json +1 -1
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
// Only an override opens the feedback popup. Silent accepts and retractions post
|
|
2
|
+
// in the background, so they must not drive the popup's loader or its toast.
|
|
3
|
+
export const isPromptedRecommendationFeedback = (outcome) => outcome === 'override_outside' || outcome === 'override_ranked';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../rootActions';
|
|
3
|
-
import {
|
|
3
|
+
import { isPromptedRecommendationFeedback, } from './recommendationPayload';
|
|
4
|
+
import { getEntityRecommendationKey, initialEntityRecommendationState, } from './recommendationState';
|
|
4
5
|
export const initialState = {
|
|
5
|
-
|
|
6
|
+
feedbackSubmitState: { fetchState: 'Not-Started', error: undefined },
|
|
6
7
|
recommendationByEntity: {},
|
|
7
8
|
};
|
|
8
9
|
const recommendation = createSlice({
|
|
@@ -138,9 +139,10 @@ const recommendation = createSlice({
|
|
|
138
139
|
// never waits on this: only the feedback popup reads the submit state.
|
|
139
140
|
submitRecommendationFeedback: {
|
|
140
141
|
reducer(draft, action) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
if (!isPromptedRecommendationFeedback(action.payload.outcome)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
draft.feedbackSubmitState = {
|
|
144
146
|
fetchState: 'In-Progress',
|
|
145
147
|
error: undefined,
|
|
146
148
|
};
|
|
@@ -151,8 +153,8 @@ const recommendation = createSlice({
|
|
|
151
153
|
},
|
|
152
154
|
submitRecommendationFeedbackCompleted: {
|
|
153
155
|
reducer(draft, action) {
|
|
154
|
-
const {
|
|
155
|
-
draft.
|
|
156
|
+
const { succeeded, error } = action.payload;
|
|
157
|
+
draft.feedbackSubmitState = {
|
|
156
158
|
fetchState: succeeded ? 'Completed' : 'Error',
|
|
157
159
|
error,
|
|
158
160
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getEntityRecommendationKey
|
|
1
|
+
import { getEntityRecommendationKey } from './recommendationState';
|
|
2
2
|
export const getRecommendationsForEntity = (state, entityId, entityType) => {
|
|
3
3
|
const { entityRecommendationState } = state;
|
|
4
4
|
const key = getEntityRecommendationKey(entityType, entityId);
|
|
@@ -11,4 +11,4 @@ export const getRecommendationsForEntity = (state, entityId, entityType) => {
|
|
|
11
11
|
recommendations,
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
-
export const getRecommendationFeedbackSubmitState = (state
|
|
14
|
+
export const getRecommendationFeedbackSubmitState = (state) => state.entityRecommendationState.feedbackSubmitState;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export const getEntityRecommendationKey = (entityType, entityIdOrName) => `${entityType}_${entityIdOrName}`;
|
|
2
|
-
export const getRecommendationFeedbackKey = (transactionId, lineId, dimension) => `${transactionId}|${lineId ?? ''}|${dimension}`;
|
|
3
2
|
export const initialEntityRecommendationState = {
|
|
4
3
|
fetchState: 'Not-Started',
|
|
5
4
|
error: undefined,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { of } from 'rxjs';
|
|
1
|
+
import { EMPTY, of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { isSuccessStatus } from '../../responsePayload';
|
|
4
|
+
import { isPromptedRecommendationFeedback, } from './recommendationPayload';
|
|
4
5
|
import { submitRecommendationFeedback, submitRecommendationFeedbackCompleted, } from './recommendationReducer';
|
|
5
|
-
import { getRecommendationFeedbackKey } from './recommendationState';
|
|
6
6
|
const toRequestBody = (payload) => ({
|
|
7
7
|
line_id: payload.lineId ?? null,
|
|
8
8
|
dimension: payload.dimension,
|
|
@@ -38,8 +38,10 @@ const toRequestBody = (payload) => ({
|
|
|
38
38
|
// user what happened. The categorization itself still never waits on this: a
|
|
39
39
|
// failure is surfaced in a toast rather than blocking or reverting the change.
|
|
40
40
|
export const submitRecommendationFeedbackEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(submitRecommendationFeedback.match), mergeMap((action) => {
|
|
41
|
-
const { transactionId
|
|
42
|
-
|
|
41
|
+
const { transactionId } = action.payload;
|
|
42
|
+
// Only the override that opened the popup reports completion; accepts and
|
|
43
|
+
// retractions stay fire-and-forget so they cannot settle the popup.
|
|
44
|
+
const isPrompted = isPromptedRecommendationFeedback(action.payload.outcome);
|
|
43
45
|
return zeniAPI
|
|
44
46
|
.postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${transactionId}/recommendation-feedback`, toRequestBody(action.payload))
|
|
45
47
|
.pipe(mergeMap((response) => {
|
|
@@ -47,13 +49,16 @@ export const submitRecommendationFeedbackEpic = (actions$, _state$, zeniAPI) =>
|
|
|
47
49
|
if (!succeeded) {
|
|
48
50
|
console.error('submitRecommendationFeedbackEpic: recommendation feedback was not recorded', response.status);
|
|
49
51
|
}
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
return isPrompted
|
|
53
|
+
? of(submitRecommendationFeedbackCompleted({
|
|
54
|
+
succeeded,
|
|
55
|
+
error: succeeded ? undefined : response.status,
|
|
56
|
+
}))
|
|
57
|
+
: EMPTY;
|
|
55
58
|
}), catchError((error) => {
|
|
56
59
|
console.error('submitRecommendationFeedbackEpic: failed to submit recommendation feedback', error);
|
|
57
|
-
return
|
|
60
|
+
return isPrompted
|
|
61
|
+
? of(submitRecommendationFeedbackCompleted({ succeeded: false }))
|
|
62
|
+
: EMPTY;
|
|
58
63
|
}));
|
|
59
64
|
}));
|
|
@@ -15,6 +15,7 @@ export type FetchRecommendationResponseWithCOT = ZeniAPIResponse<FetchRecommenda
|
|
|
15
15
|
export type RecommendationFeedbackDimension = 'vendor' | 'category' | 'class';
|
|
16
16
|
export type RecommendationFeedbackSource = 'primary' | 'alternate' | 'shadow' | 'qbo';
|
|
17
17
|
export type RecommendationFeedbackOutcome = 'accepted' | 'override_ranked' | 'override_outside' | 'retracted';
|
|
18
|
+
export declare const isPromptedRecommendationFeedback: (outcome: RecommendationFeedbackOutcome) => boolean;
|
|
18
19
|
export interface RecommendationFeedbackValue {
|
|
19
20
|
name: string;
|
|
20
21
|
id?: string | null;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPromptedRecommendationFeedback = void 0;
|
|
4
|
+
// Only an override opens the feedback popup. Silent accepts and retractions post
|
|
5
|
+
// in the background, so they must not drive the popup's loader or its toast.
|
|
6
|
+
const isPromptedRecommendationFeedback = (outcome) => outcome === 'override_outside' || outcome === 'override_ranked';
|
|
7
|
+
exports.isPromptedRecommendationFeedback = isPromptedRecommendationFeedback;
|
|
@@ -6,7 +6,7 @@ import { SupportedTransaction } from '../../entity/transaction/transactionState'
|
|
|
6
6
|
import { ZeniAPIStatus } from '../../responsePayload';
|
|
7
7
|
import { TransactionsTab } from '../expenseAutomationView/types/transactionsViewState';
|
|
8
8
|
import { SubmitRecommendationFeedbackPayload } from './recommendationPayload';
|
|
9
|
-
import { EntityRecommendationState
|
|
9
|
+
import { EntityRecommendationState } from './recommendationState';
|
|
10
10
|
export type RecommendationLineUpdateFlowType = 'categorization' | 'detail';
|
|
11
11
|
type FetchEntityRecommendationsForLineUpdateCommonPayload = {
|
|
12
12
|
amount: number;
|
|
@@ -59,11 +59,9 @@ export declare const fetchEntityRecommendationsByTransactionId: import("@reduxjs
|
|
|
59
59
|
entityIdOrName: string;
|
|
60
60
|
entityType: EntityType;
|
|
61
61
|
}, "recommendation/indicateReadyToInitializeRecommendation", never, never>, submitRecommendationFeedback: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[args: SubmitRecommendationFeedbackPayload], SubmitRecommendationFeedbackPayload, "recommendation/submitRecommendationFeedback", never, never>, submitRecommendationFeedbackCompleted: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[args: {
|
|
62
|
-
key: RecommendationFeedbackKey;
|
|
63
62
|
succeeded: boolean;
|
|
64
63
|
error?: ZeniAPIStatus;
|
|
65
64
|
}], {
|
|
66
|
-
key: RecommendationFeedbackKey;
|
|
67
65
|
succeeded: boolean;
|
|
68
66
|
error?: ZeniAPIStatus;
|
|
69
67
|
}, "recommendation/submitRecommendationFeedbackCompleted", never, never>;
|
|
@@ -4,9 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.submitRecommendationFeedbackCompleted = exports.submitRecommendationFeedback = exports.indicateReadyToInitializeRecommendation = exports.updateRecommendationForEntityFailure = exports.updateRecommendationForEntity = exports.fetchRecommendationByEntityId = exports.fetchRecommendationByEntityName = exports.fetchEntityRecommendationsForLineUpdate = exports.fetchEntityRecommendationsByTransactionId = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const rootActions_1 = require("../../rootActions");
|
|
7
|
+
const recommendationPayload_1 = require("./recommendationPayload");
|
|
7
8
|
const recommendationState_1 = require("./recommendationState");
|
|
8
9
|
exports.initialState = {
|
|
9
|
-
|
|
10
|
+
feedbackSubmitState: { fetchState: 'Not-Started', error: undefined },
|
|
10
11
|
recommendationByEntity: {},
|
|
11
12
|
};
|
|
12
13
|
const recommendation = (0, toolkit_1.createSlice)({
|
|
@@ -142,9 +143,10 @@ const recommendation = (0, toolkit_1.createSlice)({
|
|
|
142
143
|
// never waits on this: only the feedback popup reads the submit state.
|
|
143
144
|
submitRecommendationFeedback: {
|
|
144
145
|
reducer(draft, action) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
if (!(0, recommendationPayload_1.isPromptedRecommendationFeedback)(action.payload.outcome)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
draft.feedbackSubmitState = {
|
|
148
150
|
fetchState: 'In-Progress',
|
|
149
151
|
error: undefined,
|
|
150
152
|
};
|
|
@@ -155,8 +157,8 @@ const recommendation = (0, toolkit_1.createSlice)({
|
|
|
155
157
|
},
|
|
156
158
|
submitRecommendationFeedbackCompleted: {
|
|
157
159
|
reducer(draft, action) {
|
|
158
|
-
const {
|
|
159
|
-
draft.
|
|
160
|
+
const { succeeded, error } = action.payload;
|
|
161
|
+
draft.feedbackSubmitState = {
|
|
160
162
|
fetchState: succeeded ? 'Completed' : 'Error',
|
|
161
163
|
error,
|
|
162
164
|
};
|
|
@@ -9,4 +9,4 @@ export interface RecommendationByEntitySelectorView extends SelectorView {
|
|
|
9
9
|
recommendations?: Recommendation[];
|
|
10
10
|
}
|
|
11
11
|
export declare const getRecommendationsForEntity: (state: RootState, entityId: string, entityType: EntityType) => RecommendationByEntitySelectorView;
|
|
12
|
-
export declare const getRecommendationFeedbackSubmitState: (state: RootState
|
|
12
|
+
export declare const getRecommendationFeedbackSubmitState: (state: RootState) => FetchStateAndError;
|
|
@@ -15,5 +15,5 @@ const getRecommendationsForEntity = (state, entityId, entityType) => {
|
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
exports.getRecommendationsForEntity = getRecommendationsForEntity;
|
|
18
|
-
const getRecommendationFeedbackSubmitState = (state
|
|
18
|
+
const getRecommendationFeedbackSubmitState = (state) => state.entityRecommendationState.feedbackSubmitState;
|
|
19
19
|
exports.getRecommendationFeedbackSubmitState = getRecommendationFeedbackSubmitState;
|
|
@@ -3,13 +3,11 @@ import { Recommendation } from '../../commonStateTypes/recommendationBase';
|
|
|
3
3
|
import { EntityType } from '../../entity/genericEntity/entity';
|
|
4
4
|
export declare const getEntityRecommendationKey: (entityType: EntityType, entityIdOrName: string) => `vendor_${string}` | `customer_${string}` | `merchant_${string}`;
|
|
5
5
|
export type EntityRecommendationKey = ReturnType<typeof getEntityRecommendationKey>;
|
|
6
|
-
export declare const getRecommendationFeedbackKey: (transactionId: string, lineId: string | null | undefined, dimension: string) => `${string}|${string}|${string}`;
|
|
7
|
-
export type RecommendationFeedbackKey = ReturnType<typeof getRecommendationFeedbackKey>;
|
|
8
6
|
export interface EntityRecommendationsStatus extends FetchedState {
|
|
9
7
|
recommendations?: Recommendation[];
|
|
10
8
|
}
|
|
11
9
|
export declare const initialEntityRecommendationState: EntityRecommendationsStatus;
|
|
12
10
|
export interface EntityRecommendationState {
|
|
13
|
-
|
|
11
|
+
feedbackSubmitState: FetchStateAndError;
|
|
14
12
|
recommendationByEntity: Record<EntityRecommendationKey, EntityRecommendationsStatus>;
|
|
15
13
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initialEntityRecommendationState = exports.
|
|
3
|
+
exports.initialEntityRecommendationState = exports.getEntityRecommendationKey = void 0;
|
|
4
4
|
const getEntityRecommendationKey = (entityType, entityIdOrName) => `${entityType}_${entityIdOrName}`;
|
|
5
5
|
exports.getEntityRecommendationKey = getEntityRecommendationKey;
|
|
6
|
-
const getRecommendationFeedbackKey = (transactionId, lineId, dimension) => `${transactionId}|${lineId ?? ''}|${dimension}`;
|
|
7
|
-
exports.getRecommendationFeedbackKey = getRecommendationFeedbackKey;
|
|
8
6
|
exports.initialEntityRecommendationState = {
|
|
9
7
|
fetchState: 'Not-Started',
|
|
10
8
|
error: undefined,
|
|
@@ -4,8 +4,8 @@ exports.submitRecommendationFeedbackEpic = void 0;
|
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const responsePayload_1 = require("../../responsePayload");
|
|
7
|
+
const recommendationPayload_1 = require("./recommendationPayload");
|
|
7
8
|
const recommendationReducer_1 = require("./recommendationReducer");
|
|
8
|
-
const recommendationState_1 = require("./recommendationState");
|
|
9
9
|
const toRequestBody = (payload) => ({
|
|
10
10
|
line_id: payload.lineId ?? null,
|
|
11
11
|
dimension: payload.dimension,
|
|
@@ -41,8 +41,10 @@ const toRequestBody = (payload) => ({
|
|
|
41
41
|
// user what happened. The categorization itself still never waits on this: a
|
|
42
42
|
// failure is surfaced in a toast rather than blocking or reverting the change.
|
|
43
43
|
const submitRecommendationFeedbackEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(recommendationReducer_1.submitRecommendationFeedback.match), (0, operators_1.mergeMap)((action) => {
|
|
44
|
-
const { transactionId
|
|
45
|
-
|
|
44
|
+
const { transactionId } = action.payload;
|
|
45
|
+
// Only the override that opened the popup reports completion; accepts and
|
|
46
|
+
// retractions stay fire-and-forget so they cannot settle the popup.
|
|
47
|
+
const isPrompted = (0, recommendationPayload_1.isPromptedRecommendationFeedback)(action.payload.outcome);
|
|
46
48
|
return zeniAPI
|
|
47
49
|
.postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${transactionId}/recommendation-feedback`, toRequestBody(action.payload))
|
|
48
50
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
@@ -50,14 +52,17 @@ const submitRecommendationFeedbackEpic = (actions$, _state$, zeniAPI) => actions
|
|
|
50
52
|
if (!succeeded) {
|
|
51
53
|
console.error('submitRecommendationFeedbackEpic: recommendation feedback was not recorded', response.status);
|
|
52
54
|
}
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
return isPrompted
|
|
56
|
+
? (0, rxjs_1.of)((0, recommendationReducer_1.submitRecommendationFeedbackCompleted)({
|
|
57
|
+
succeeded,
|
|
58
|
+
error: succeeded ? undefined : response.status,
|
|
59
|
+
}))
|
|
60
|
+
: rxjs_1.EMPTY;
|
|
58
61
|
}), (0, operators_1.catchError)((error) => {
|
|
59
62
|
console.error('submitRecommendationFeedbackEpic: failed to submit recommendation feedback', error);
|
|
60
|
-
return
|
|
63
|
+
return isPrompted
|
|
64
|
+
? (0, rxjs_1.of)((0, recommendationReducer_1.submitRecommendationFeedbackCompleted)({ succeeded: false }))
|
|
65
|
+
: rxjs_1.EMPTY;
|
|
61
66
|
}));
|
|
62
67
|
}));
|
|
63
68
|
exports.submitRecommendationFeedbackEpic = submitRecommendationFeedbackEpic;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.58-
|
|
3
|
+
"version": "5.2.58-beta1ND",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|