@zeniai/client-epic-state 4.19.84-betaVR6 → 4.19.84-betaVR8
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/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +18 -1
- package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +31 -1
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +12 -4
- package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +6 -0
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +7 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +18 -1
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +11 -0
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +32 -1
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +12 -4
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +1 -0
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +7 -1
- package/package.json +1 -1
|
@@ -15,9 +15,13 @@ const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0,
|
|
|
15
15
|
.postFormData(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipts/bulk-upload`, formData)
|
|
16
16
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
17
17
|
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const batchId = String(response.data.batch_id ?? '').trim();
|
|
19
|
+
if (batchId === '') {
|
|
20
|
+
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({
|
|
21
|
+
error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Bulk upload succeeded but batch_id was missing or empty.'),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess)({ batchId }));
|
|
21
25
|
}
|
|
22
26
|
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({ error: response.status }));
|
|
23
27
|
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
3
4
|
import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
|
|
4
5
|
import { RootState } from '../../../../reducer';
|
|
5
6
|
import { ZeniAPI } from '../../../../zeniAPI';
|
|
6
7
|
import { bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation } from '../../reducers/missingReceiptsViewReducer';
|
|
7
|
-
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof requestMissingReceiptsTabNavigation> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload>;
|
|
8
|
+
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof requestMissingReceiptsTabNavigation> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload> | ReturnType<typeof openSnackbar>;
|
|
8
9
|
/**
|
|
9
10
|
* On Pusher batch completion: refresh batch list (then fetchMultipleBatchDetailsEpic runs),
|
|
10
11
|
* and request switching to the Unmatched tab. Debounced to reduce duplicate refreshes.
|
|
11
12
|
*/
|
|
12
13
|
export declare const pusherBatchStatusCompletionEpic: (actions$: ActionsObservable<ActionType>) => Observable<ActionType>;
|
|
14
|
+
/**
|
|
15
|
+
* Fallback polling when Pusher is slow or unavailable. Network errors on a poll tick show one
|
|
16
|
+
* error snackbar per upload session (first failure only) so the user is not spammed every
|
|
17
|
+
* interval; Pusher or a later successful poll still completes the flow.
|
|
18
|
+
*/
|
|
13
19
|
export declare const pollBulkUploadBatchStatusEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.pollBulkUploadBatchStatusEpic = exports.pusherBatchStatusCompletionEpic = void 0;
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
|
|
6
7
|
const transactionReducer_1 = require("../../../../entity/transaction/transactionReducer");
|
|
7
8
|
const responsePayload_1 = require("../../../../responsePayload");
|
|
8
9
|
const missingReceiptsPayload_1 = require("../../payload/missingReceiptsPayload");
|
|
@@ -22,8 +23,14 @@ const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe((0, operator
|
|
|
22
23
|
(0, missingReceiptsViewReducer_1.requestMissingReceiptsTabNavigation)({ tab: 'unmatched' }),
|
|
23
24
|
])));
|
|
24
25
|
exports.pusherBatchStatusCompletionEpic = pusherBatchStatusCompletionEpic;
|
|
26
|
+
/**
|
|
27
|
+
* Fallback polling when Pusher is slow or unavailable. Network errors on a poll tick show one
|
|
28
|
+
* error snackbar per upload session (first failure only) so the user is not spammed every
|
|
29
|
+
* interval; Pusher or a later successful poll still completes the flow.
|
|
30
|
+
*/
|
|
25
31
|
const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.mergeMap)((action) => {
|
|
26
32
|
const { batchId } = action.payload;
|
|
33
|
+
let pollErrorNotified = false;
|
|
27
34
|
return (0, rxjs_1.timer)(FALLBACK_FIRST_DELAY_MS, FALLBACK_POLL_INTERVAL_MS).pipe((0, operators_1.takeUntil)((0, rxjs_1.merge)(actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.clearBulkUpload.match)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((a) => a.payload.batchId === batchId)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsSuccess.match), (0, operators_1.filter)((a) => a.payload.batchId === batchId)))), (0, operators_1.switchMap)(() => zeniAPI
|
|
28
35
|
.getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${batchId}`)
|
|
29
36
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
@@ -42,6 +49,16 @@ const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.p
|
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
return rxjs_1.EMPTY;
|
|
45
|
-
}), (0, operators_1.catchError)(() =>
|
|
52
|
+
}), (0, operators_1.catchError)(() => {
|
|
53
|
+
if (!pollErrorNotified) {
|
|
54
|
+
pollErrorNotified = true;
|
|
55
|
+
return (0, rxjs_1.of)((0, snackbarReducer_1.openSnackbar)({
|
|
56
|
+
messageSection: 'receipts_upload',
|
|
57
|
+
messageText: 'failed',
|
|
58
|
+
type: 'error',
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
return rxjs_1.EMPTY;
|
|
62
|
+
}))));
|
|
46
63
|
}));
|
|
47
64
|
exports.pollBulkUploadBatchStatusEpic = pollBulkUploadBatchStatusEpic;
|
|
@@ -78,6 +78,17 @@ export interface BatchDetailsResponsePayload {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
export type BatchDetailsResponse = ZeniAPIResponse<BatchDetailsResponsePayload>;
|
|
81
|
+
/**
|
|
82
|
+
* Maps batch-details API `files[].status` strings to canonical {@link BatchFile} statuses.
|
|
83
|
+
* The service may send aliases (`unmatched`, `possible_match`) or different separators; the UI
|
|
84
|
+
* only treats `un_matched` / `no_match` as work for the Unmatched tab.
|
|
85
|
+
*
|
|
86
|
+
* **Unknown values:** Any string not matched below is returned as-is (typed as
|
|
87
|
+
* {@link BatchFile} status). Callers should not assume exhaustiveness—treat unknown statuses in
|
|
88
|
+
* UI as “other” (e.g. show a generic label or fall back to a safe bucket) until the API contract
|
|
89
|
+
* adds an explicit mapping.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeBatchFileStatus(raw: string): BatchFile['status'];
|
|
81
92
|
export declare function toBatchFile(payload: BatchFilePayload, filesEndPoint?: string): BatchFile;
|
|
82
93
|
export declare function extractTransactionPayloadsFromBatchFiles(files: BatchFilePayload[]): SupportedTransactionPayload[];
|
|
83
94
|
export declare function toBatchSummary(payload: BatchDetailsResponsePayload['summary']): BatchSummary;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeBatchFileStatus = normalizeBatchFileStatus;
|
|
3
4
|
exports.toBatchFile = toBatchFile;
|
|
4
5
|
exports.extractTransactionPayloadsFromBatchFiles = extractTransactionPayloadsFromBatchFiles;
|
|
5
6
|
exports.toBatchSummary = toBatchSummary;
|
|
@@ -14,6 +15,36 @@ exports.supportedTransactionPayloadToManualSearchResult = supportedTransactionPa
|
|
|
14
15
|
const urlPayload_1 = require("../../../commonPayloadTypes/urlPayload");
|
|
15
16
|
const transactionPayload_1 = require("../../../entity/transaction/payloadTypes/transactionPayload");
|
|
16
17
|
// -- Converter functions --
|
|
18
|
+
/**
|
|
19
|
+
* Maps batch-details API `files[].status` strings to canonical {@link BatchFile} statuses.
|
|
20
|
+
* The service may send aliases (`unmatched`, `possible_match`) or different separators; the UI
|
|
21
|
+
* only treats `un_matched` / `no_match` as work for the Unmatched tab.
|
|
22
|
+
*
|
|
23
|
+
* **Unknown values:** Any string not matched below is returned as-is (typed as
|
|
24
|
+
* {@link BatchFile} status). Callers should not assume exhaustiveness—treat unknown statuses in
|
|
25
|
+
* UI as “other” (e.g. show a generic label or fall back to a safe bucket) until the API contract
|
|
26
|
+
* adds an explicit mapping.
|
|
27
|
+
*/
|
|
28
|
+
function normalizeBatchFileStatus(raw) {
|
|
29
|
+
const s = raw.trim().toLowerCase().replace(/-/g, '_');
|
|
30
|
+
switch (s) {
|
|
31
|
+
case 'un_matched':
|
|
32
|
+
case 'unmatched':
|
|
33
|
+
return 'un_matched';
|
|
34
|
+
case 'no_match':
|
|
35
|
+
case 'nomatch':
|
|
36
|
+
return 'no_match';
|
|
37
|
+
case 'matched':
|
|
38
|
+
return 'matched';
|
|
39
|
+
case 'failed':
|
|
40
|
+
return 'failed';
|
|
41
|
+
case 'possible_match':
|
|
42
|
+
case 'possiblematch':
|
|
43
|
+
return 'un_matched';
|
|
44
|
+
default:
|
|
45
|
+
return raw;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
17
48
|
function toBatchFile(payload, filesEndPoint) {
|
|
18
49
|
return {
|
|
19
50
|
attachmentId: payload.attachment_id,
|
|
@@ -29,7 +60,7 @@ function toBatchFile(payload, filesEndPoint) {
|
|
|
29
60
|
filename: payload.filename,
|
|
30
61
|
matchSource: payload.match_source,
|
|
31
62
|
matchedTransactionId: payload.matched_transaction?.transaction_id,
|
|
32
|
-
status: payload.status,
|
|
63
|
+
status: normalizeBatchFileStatus(payload.status),
|
|
33
64
|
};
|
|
34
65
|
}
|
|
35
66
|
function extractTransactionPayloadsFromBatchFiles(files) {
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.fetchCompletedTransactionsFailure = exports.fetchCompletedTransactionsSuccess = exports.fetchCompletedTransactions = exports.acknowledgeBulkUploadConfirmMatchComplete = exports.clearManualSearchResults = exports.searchTransactionsForManualMatchFailure = exports.searchTransactionsForManualMatchSuccess = exports.searchTransactionsForManualMatch = exports.clearBulkUpload = exports.setBulkUploadSortConfig = exports.setBulkUploadCompletedSubTab = exports.setBulkUploadResultsTab = exports.confirmBulkUploadMatchFailure = exports.confirmBulkUploadMatchSuccess = exports.confirmBulkUploadMatch = exports.fetchBulkUploadBatchesFailure = exports.fetchBulkUploadBatchesSuccess = exports.fetchBulkUploadBatches = exports.fetchMoreBatchDetailsFailure = exports.fetchMoreBatchDetailsComplete = exports.fetchMoreBatchDetails = exports.setInitialBatchDetailsLoading = exports.batchDetailFetchFailed = exports.storeBatchDetails = exports.fetchBulkUploadBatchDetailsFailure = exports.fetchBulkUploadBatchDetailsSuccess = exports.fetchBulkUploadBatchDetails = exports.clearMissingReceiptsTabNavigation = exports.requestMissingReceiptsTabNavigation = exports.pusherBatchStatusUpdate = exports.bulkUploadReceiptsFailure = exports.bulkUploadReceiptsSuccess = exports.updateBulkUploadProgress = exports.bulkUploadReceipts = exports.clearExpenseAutomationMissingReceiptsView = exports.markMissingReceiptAsDone = exports.updateMissingReceiptsUIState = exports.updateMissingReceiptUploadState = exports.uploadMissingReceiptSuccess = exports.fetchMissingReceiptsFailure = exports.fetchMissingReceiptsSuccess = exports.fetchMissingReceipts = exports.initialState = exports.initialBulkUploadState = exports.getCompletedTransactionsCacheKey = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
7
|
+
const missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
|
|
7
8
|
const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
|
|
8
9
|
exports.getCompletedTransactionsCacheKey = getCompletedTransactionsCacheKey;
|
|
9
10
|
exports.initialBulkUploadState = {
|
|
@@ -391,13 +392,20 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
391
392
|
const isLoadMore = pageToken != null;
|
|
392
393
|
const trimmed = query.trim();
|
|
393
394
|
/**
|
|
394
|
-
* Cleared or too-short query: no API (see epic). Reset fully (no In-Progress)
|
|
395
|
-
*
|
|
395
|
+
* Cleared or too-short query: no API (see epic). Reset fully (no In-Progress).
|
|
396
|
+
* Only bump `manualSearchUiResetKey` when clearing a real search — not when the UI
|
|
397
|
+
* dispatches `""` on every keystroke while length is below MIN (web-components always sends
|
|
398
|
+
* `onSearch("")` in that case), or ReceiptCard remounts and the input loses focus.
|
|
396
399
|
*/
|
|
397
400
|
if (!isLoadMore &&
|
|
398
|
-
trimmed.length <
|
|
401
|
+
trimmed.length < missingReceiptsViewState_1.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH) {
|
|
402
|
+
const hadActiveSearch = draft.bulkUpload.manualSearch.searchQuery.trim().length >=
|
|
403
|
+
missingReceiptsViewState_1.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH ||
|
|
404
|
+
draft.bulkUpload.manualSearch.results.length > 0;
|
|
399
405
|
draft.bulkUpload.manualSearch = exports.initialBulkUploadState.manualSearch;
|
|
400
|
-
|
|
406
|
+
if (hadActiveSearch) {
|
|
407
|
+
draft.bulkUpload.manualSearchUiResetKey += 1;
|
|
408
|
+
}
|
|
401
409
|
return;
|
|
402
410
|
}
|
|
403
411
|
draft.bulkUpload.manualSearch.searchQuery = query;
|
|
@@ -17,6 +17,7 @@ export interface MissingReceiptsViewUIState {
|
|
|
17
17
|
* Minimum trimmed query length before manual-match search calls the expense-automation API.
|
|
18
18
|
* Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
|
|
19
19
|
*/
|
|
20
|
+
export declare const MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
|
|
20
21
|
export type BatchStatusValue = 'pending' | 'processing' | 'completed';
|
|
21
22
|
export type BatchFileStatus = 'matched' | 'un_matched' | 'no_match' | 'failed';
|
|
22
23
|
export type BulkUploadPhase = 'idle' | 'uploading' | 'matching' | 'completed';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toMissingReceiptsSortKey = void 0;
|
|
3
|
+
exports.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = exports.toMissingReceiptsSortKey = void 0;
|
|
4
4
|
const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
|
|
5
5
|
const MISSING_RECEIPTS_SORT_KEYS = [
|
|
6
6
|
'vendor',
|
|
@@ -11,3 +11,9 @@ const MISSING_RECEIPTS_SORT_KEYS = [
|
|
|
11
11
|
];
|
|
12
12
|
const toMissingReceiptsSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, MISSING_RECEIPTS_SORT_KEYS);
|
|
13
13
|
exports.toMissingReceiptsSortKey = toMissingReceiptsSortKey;
|
|
14
|
+
// -- Bulk Receipt Upload Types --
|
|
15
|
+
/**
|
|
16
|
+
* Minimum trimmed query length before manual-match search calls the expense-automation API.
|
|
17
|
+
* Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
|
|
18
|
+
*/
|
|
19
|
+
exports.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "4.19.84-
|
|
3
|
+
"version": "4.19.84-betaVR8",
|
|
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",
|