@zeniai/client-epic-state 5.1.57-betaDI0 → 5.1.57-betaRD1
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/coreEpics.js +2 -3
- package/lib/entity/chargeCard/chargeCard.d.ts +1 -1
- package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +2 -0
- package/lib/entity/jeSchedules/jeSchedulesPayload.js +2 -0
- package/lib/entity/jeSchedules/jeSchedulesSelector.d.ts +4 -0
- package/lib/entity/jeSchedules/jeSchedulesSelector.js +11 -1
- package/lib/entity/jeSchedules/jeSchedulesState.d.ts +2 -0
- package/lib/entity/tenant/clearAllEpic.d.ts +1 -3
- package/lib/entity/tenant/clearAllEpic.js +0 -4
- package/lib/entity/transaction/scheduleTransactionHelper.d.ts +15 -0
- package/lib/entity/transaction/scheduleTransactionHelper.js +43 -0
- package/lib/epic.d.ts +5 -2
- package/lib/epic.js +5 -2
- package/lib/esm/coreEpics.js +2 -3
- package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +2 -0
- package/lib/esm/entity/jeSchedules/jeSchedulesSelector.js +11 -1
- package/lib/esm/entity/tenant/clearAllEpic.js +0 -4
- package/lib/esm/entity/transaction/scheduleTransactionHelper.js +39 -0
- package/lib/esm/epic.js +5 -2
- package/lib/esm/index.js +6 -9
- package/lib/esm/reducer.js +0 -6
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +15 -2
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.js +21 -0
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +110 -0
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.js +53 -0
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +22 -0
- package/lib/esm/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.js +152 -0
- package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +126 -6
- package/lib/esm/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.js +1 -0
- package/lib/esm/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.js +59 -0
- package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +65 -11
- package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -6
- package/lib/esm/view/settingsView/epic/fetchNotificationSettingsEpic.js +2 -21
- package/lib/esm/view/settingsView/epic/saveNotificationSettingsEpic.js +1 -2
- package/lib/esm/view/settingsView/settingsViewHelpers.js +2 -3
- package/lib/index.d.ts +8 -12
- package/lib/index.js +56 -55
- package/lib/reducer.d.ts +0 -6
- package/lib/reducer.js +0 -6
- package/lib/view/common/recurringViewHelper.d.ts +2 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +14 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.d.ts +8 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.js +25 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts +45 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +114 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.d.ts +8 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.js +57 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.d.ts +6 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +26 -0
- package/lib/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.d.ts +43 -0
- package/lib/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.js +158 -0
- package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.d.ts +1 -0
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +36 -8
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +128 -7
- package/lib/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.d.ts +23 -0
- package/lib/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.js +2 -0
- package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +29 -4
- package/lib/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.d.ts +3 -0
- package/lib/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.js +63 -0
- package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +65 -11
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +53 -6
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +5 -8
- package/lib/view/settingsView/epic/fetchNotificationSettingsEpic.d.ts +1 -9
- package/lib/view/settingsView/epic/fetchNotificationSettingsEpic.js +2 -21
- package/lib/view/settingsView/epic/saveNotificationSettingsEpic.js +1 -2
- package/lib/view/settingsView/settingsViewHelpers.d.ts +1 -1
- package/lib/view/settingsView/settingsViewHelpers.js +2 -3
- package/lib/view/settingsView/settingsViewPayload.d.ts +0 -4
- package/lib/view/settingsView/settingsViewState.d.ts +1 -1
- package/package.json +1 -1
- package/lib/entity/notificationRegistry/notificationRegistryPayload.d.ts +0 -18
- package/lib/entity/notificationRegistry/notificationRegistryPayload.js +0 -28
- package/lib/entity/notificationRegistry/notificationRegistryReducer.d.ts +0 -6
- package/lib/entity/notificationRegistry/notificationRegistryReducer.js +0 -26
- package/lib/entity/notificationRegistry/notificationRegistrySelector.d.ts +0 -2
- package/lib/entity/notificationRegistry/notificationRegistrySelector.js +0 -10
- package/lib/entity/notificationRegistry/notificationRegistryState.d.ts +0 -31
- package/lib/entity/notificationRegistry/notificationRegistryState.js +0 -10
- package/lib/esm/entity/notificationRegistry/notificationRegistryPayload.js +0 -24
- package/lib/esm/entity/notificationRegistry/notificationRegistryReducer.js +0 -22
- package/lib/esm/entity/notificationRegistry/notificationRegistrySelector.js +0 -6
- package/lib/esm/entity/notificationRegistry/notificationRegistryState.js +0 -5
- package/lib/esm/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +0 -103
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesEndpoint.js +0 -5
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewPayload.js +0 -36
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewReducer.js +0 -222
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewSelector.js +0 -25
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewState.js +0 -4
- package/lib/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.d.ts +0 -34
- package/lib/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +0 -107
- package/lib/view/notificationPreferencesView/notificationPreferencesEndpoint.d.ts +0 -2
- package/lib/view/notificationPreferencesView/notificationPreferencesEndpoint.js +0 -9
- package/lib/view/notificationPreferencesView/notificationPreferencesViewPayload.d.ts +0 -7
- package/lib/view/notificationPreferencesView/notificationPreferencesViewPayload.js +0 -41
- package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.d.ts +0 -30
- package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.js +0 -226
- package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.d.ts +0 -7
- package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.js +0 -32
- package/lib/view/notificationPreferencesView/notificationPreferencesViewState.d.ts +0 -14
- package/lib/view/notificationPreferencesView/notificationPreferencesViewState.js +0 -8
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { NestedAccountHierarchyForReport } from '../../../commonStateTypes/accountView/nestedAccountID';
|
|
2
|
+
import { Amount } from '../../../commonStateTypes/amount';
|
|
2
3
|
import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
|
|
3
4
|
import { Recommendation } from '../../../commonStateTypes/recommendationBase';
|
|
4
5
|
import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
|
|
5
|
-
import { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
6
|
+
import { MonthYearPeriodId, ScheduleDaysOfMonth } from '../../../commonStateTypes/timePeriod';
|
|
6
7
|
import { AccountBase } from '../../../entity/account/accountState';
|
|
7
|
-
import { JEScheduleTransactionKey, ScheduleTypes } from '../../../entity/jeSchedules/jeSchedulesTypes';
|
|
8
|
+
import { JEScheduleTransactionKey, ScheduleJournalEntryStatusCodeType, ScheduleTypes } from '../../../entity/jeSchedules/jeSchedulesTypes';
|
|
9
|
+
import { TransactionID } from '../../../entity/transaction/stateTypes/transaction';
|
|
8
10
|
import { VendorBase } from '../../../entity/vendor/vendorState';
|
|
9
|
-
|
|
11
|
+
import { ZeniDate } from '../../../zeniDayJS';
|
|
12
|
+
import { JETransactionLink } from '../../scheduleView/scheduleDetailView/scheduleDetailState';
|
|
13
|
+
export declare const toJEScheduleSortKey: (v: string) => "vendor" | "type" | "category" | "class" | "memo" | "transactionDate" | "totalAmount" | "runningBalance" | "jePostingDate" | "depCategory" | "scheduleCategory" | "startMonth" | "amortizationPeriod" | "remainingMonths";
|
|
10
14
|
export type JEScheduleSortKey = ReturnType<typeof toJEScheduleSortKey>;
|
|
11
|
-
export declare const
|
|
12
|
-
export type
|
|
15
|
+
export declare const toJEPageTab: (v: string) => "schedules" | "existing_schedules";
|
|
16
|
+
export type JEPageTab = ReturnType<typeof toJEPageTab>;
|
|
13
17
|
export interface JESchedulesViewUIState {
|
|
18
|
+
createdGroupCollapsed: boolean;
|
|
19
|
+
pendingGroupCollapsed: boolean;
|
|
14
20
|
scrollPosition: {
|
|
15
21
|
scrollTop: number;
|
|
16
22
|
scrollLeft?: number;
|
|
17
23
|
} | undefined;
|
|
18
24
|
searchString: string;
|
|
19
|
-
|
|
25
|
+
selectedJETypeFilter: ScheduleTypes | null;
|
|
26
|
+
selectedPageTab: JEPageTab;
|
|
20
27
|
sortKey: JEScheduleSortKey;
|
|
21
28
|
sortOrder: SortOrder;
|
|
22
29
|
totalCount: number;
|
|
@@ -37,10 +44,50 @@ export interface JESchedulesViewState extends FetchedState {
|
|
|
37
44
|
ignoreStatusById: Record<JEScheduleTransactionKey, FetchStateAndError>;
|
|
38
45
|
jeScheduleLocalDataById: Record<ID, JEScheduleLocalData>;
|
|
39
46
|
jeScheduleTransactionKeysByPeriod: Record<MonthYearPeriodId, JEScheduleTransactionKey[]>;
|
|
47
|
+
newSchedule: NewJeScheduleState;
|
|
40
48
|
postStatusById: Record<ID, FetchStateAndError>;
|
|
41
49
|
refreshStatus: FetchStateAndError;
|
|
42
50
|
uiState: JESchedulesViewUIState;
|
|
43
51
|
}
|
|
52
|
+
export interface NewJeDetailLocalData {
|
|
53
|
+
amount: Amount;
|
|
54
|
+
jeLink: JETransactionLink;
|
|
55
|
+
memo: string;
|
|
56
|
+
postingDate: ZeniDate;
|
|
57
|
+
postingStatus: ScheduleJournalEntryStatusCodeType;
|
|
58
|
+
runningBalance: Amount;
|
|
59
|
+
}
|
|
60
|
+
export interface NewScheduleLocalData {
|
|
61
|
+
jeScheduleDetails: NewJeDetailLocalData[];
|
|
62
|
+
scheduleType: ScheduleTypes;
|
|
63
|
+
lastDayOfMonth?: boolean;
|
|
64
|
+
selectedAccount?: ID;
|
|
65
|
+
selectedBaseTransactionId?: TransactionID;
|
|
66
|
+
selectedClass?: ID;
|
|
67
|
+
selectedDate?: ZeniDate;
|
|
68
|
+
selectedDayOfMonth?: ScheduleDaysOfMonth;
|
|
69
|
+
selectedJEScheduleKey?: JEScheduleTransactionKey;
|
|
70
|
+
selectedMonths?: number;
|
|
71
|
+
selectedVendor?: VendorBase;
|
|
72
|
+
}
|
|
73
|
+
export interface NewScheduleLocalDataFixedAssets extends NewScheduleLocalData {
|
|
74
|
+
assetId?: string;
|
|
75
|
+
selectedAccumulatedDepreciationCategory?: ID;
|
|
76
|
+
}
|
|
77
|
+
export interface SearchTransactionsState extends FetchStateAndError {
|
|
78
|
+
searchResults: ID[];
|
|
79
|
+
searchString?: string;
|
|
80
|
+
}
|
|
81
|
+
export declare const MIN_NEW_SCHEDULE_SEARCH_LENGTH = 2;
|
|
82
|
+
export interface NewJeScheduleState extends FetchStateAndError {
|
|
83
|
+
newScheduleState: {
|
|
84
|
+
fixed_assets: NewScheduleLocalDataFixedAssets;
|
|
85
|
+
prepaid_expenses: NewScheduleLocalData;
|
|
86
|
+
};
|
|
87
|
+
saveStatus: FetchStateAndError;
|
|
88
|
+
searchTransactions: SearchTransactionsState;
|
|
89
|
+
selectedDetailScheduleType?: ScheduleTypes;
|
|
90
|
+
}
|
|
44
91
|
export interface AccountSettingsLocalData {
|
|
45
92
|
previousAccruedExpensesAccount: AccountBase[];
|
|
46
93
|
previousFixedAssetsAccount: AccountBase[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.MIN_NEW_SCHEDULE_SEARCH_LENGTH = exports.toJEPageTab = exports.toJEScheduleSortKey = void 0;
|
|
4
4
|
const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
|
|
5
5
|
const JE_SCHEDULE_SORT_KEYS = [
|
|
6
6
|
'vendor',
|
|
@@ -20,10 +20,7 @@ const JE_SCHEDULE_SORT_KEYS = [
|
|
|
20
20
|
];
|
|
21
21
|
const toJEScheduleSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, JE_SCHEDULE_SORT_KEYS);
|
|
22
22
|
exports.toJEScheduleSortKey = toJEScheduleSortKey;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
];
|
|
28
|
-
const toJEScheduleMainTab = (v) => (0, stringToUnion_1.stringToUnion)(v, JE_SCHEDULE_MAIN_TABS);
|
|
29
|
-
exports.toJEScheduleMainTab = toJEScheduleMainTab;
|
|
23
|
+
const JE_PAGE_TABS = ['schedules', 'existing_schedules'];
|
|
24
|
+
const toJEPageTab = (v) => (0, stringToUnion_1.stringToUnion)(v, JE_PAGE_TABS);
|
|
25
|
+
exports.toJEPageTab = toJEPageTab;
|
|
26
|
+
exports.MIN_NEW_SCHEDULE_SEARCH_LENGTH = 2;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
-
import { updateNotificationRegistry } from '../../../entity/notificationRegistry/notificationRegistryReducer';
|
|
3
2
|
import { RootState } from '../../../reducer';
|
|
4
3
|
import { ZeniAPI } from '../../../zeniAPI';
|
|
5
|
-
import { fetchNotificationPreferencesSuccess } from '../../notificationPreferencesView/notificationPreferencesViewReducer';
|
|
6
4
|
import { fetchNotificationSettings, updateNotificationSettingsOnFailure, updateNotificationSettingsOnSuccess } from '../settingsViewReducer';
|
|
7
|
-
export type ActionType = ReturnType<typeof fetchNotificationSettings> | ReturnType<typeof updateNotificationSettingsOnSuccess> | ReturnType<typeof updateNotificationSettingsOnFailure
|
|
5
|
+
export type ActionType = ReturnType<typeof fetchNotificationSettings> | ReturnType<typeof updateNotificationSettingsOnSuccess> | ReturnType<typeof updateNotificationSettingsOnFailure>;
|
|
8
6
|
export declare const fetchNotificationSettingsEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<{
|
|
9
7
|
payload: undefined;
|
|
10
8
|
type: "settings/fetchNotificationSettings";
|
|
@@ -17,10 +15,4 @@ export declare const fetchNotificationSettingsEpic: (actions$: ActionsObservable
|
|
|
17
15
|
} | {
|
|
18
16
|
payload: import("../../../responsePayload").ZeniAPIStatus<Record<string, unknown>>;
|
|
19
17
|
type: "settings/updateNotificationSettingsOnFailure";
|
|
20
|
-
} | {
|
|
21
|
-
payload: import("../../../entity/notificationRegistry/notificationRegistryPayload").NotificationRegistryPayload;
|
|
22
|
-
type: "notificationRegistry/updateNotificationRegistry";
|
|
23
|
-
} | {
|
|
24
|
-
payload: import("../../notificationPreferencesView/notificationPreferencesViewReducer").FetchNotificationPreferencesSuccessPayload;
|
|
25
|
-
type: "notificationPreferencesView/fetchNotificationPreferencesSuccess";
|
|
26
18
|
}>;
|
|
@@ -3,39 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fetchNotificationSettingsEpic = void 0;
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
|
-
const notificationRegistryReducer_1 = require("../../../entity/notificationRegistry/notificationRegistryReducer");
|
|
7
6
|
const responsePayload_1 = require("../../../responsePayload");
|
|
8
|
-
const notificationPreferencesEndpoint_1 = require("../../notificationPreferencesView/notificationPreferencesEndpoint");
|
|
9
|
-
const notificationPreferencesViewReducer_1 = require("../../notificationPreferencesView/notificationPreferencesViewReducer");
|
|
10
7
|
const settingsViewReducer_1 = require("../settingsViewReducer");
|
|
11
8
|
const fetchNotificationSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.fetchNotificationSettings.match), (0, operators_1.switchMap)(() => {
|
|
12
9
|
const state = state$.value;
|
|
10
|
+
const URL = `${zeniAPI.apiEndPoints.notificationMicroServiceBaseUrl}/1.0/notification-preferences`;
|
|
13
11
|
const userEmail = state.tenantState.loggedInUser?.userEmail;
|
|
14
12
|
if (userEmail == null) {
|
|
15
13
|
return (0, rxjs_1.from)([]);
|
|
16
14
|
}
|
|
17
|
-
|
|
18
|
-
// the captured value against the current `saveEpoch` on response and
|
|
19
|
-
// skips the `preferences` replace when a save has landed in the
|
|
20
|
-
// meantime — the GET is carrying the pre-PUT snapshot and would
|
|
21
|
-
// otherwise clobber the fresh save. Cursor Bugbot 3632510237.
|
|
22
|
-
const saveEpochAtDispatch = state.notificationPreferencesViewState.saveEpoch;
|
|
23
|
-
return zeniAPI
|
|
24
|
-
.getJSON((0, notificationPreferencesEndpoint_1.notificationPreferencesUrl)(zeniAPI))
|
|
25
|
-
.pipe((0, operators_1.mergeMap)((response) => {
|
|
15
|
+
return zeniAPI.getJSON(URL).pipe((0, operators_1.mergeMap)((response) => {
|
|
26
16
|
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
27
17
|
const updateActions = [
|
|
28
18
|
(0, settingsViewReducer_1.updateNotificationSettingsOnSuccess)(response.data, userEmail),
|
|
29
19
|
];
|
|
30
|
-
if (response.data.task_checklist_registry != null) {
|
|
31
|
-
updateActions.push((0, notificationRegistryReducer_1.updateNotificationRegistry)(response.data.task_checklist_registry));
|
|
32
|
-
}
|
|
33
|
-
if (response.data.task_checklist_preferences != null) {
|
|
34
|
-
updateActions.push((0, notificationPreferencesViewReducer_1.fetchNotificationPreferencesSuccess)({
|
|
35
|
-
preferences: response.data.task_checklist_preferences,
|
|
36
|
-
saveEpochAtDispatch,
|
|
37
|
-
}));
|
|
38
|
-
}
|
|
39
20
|
return (0, rxjs_1.of)(...updateActions);
|
|
40
21
|
}
|
|
41
22
|
else {
|
|
@@ -5,10 +5,9 @@ const rxjs_1 = require("rxjs");
|
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const snackbarReducer_1 = require("../../../entity/snackbar/snackbarReducer");
|
|
7
7
|
const responsePayload_1 = require("../../../responsePayload");
|
|
8
|
-
const notificationPreferencesEndpoint_1 = require("../../notificationPreferencesView/notificationPreferencesEndpoint");
|
|
9
8
|
const settingsViewReducer_1 = require("../settingsViewReducer");
|
|
10
9
|
const saveNotificationSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.saveNotificationSettings.match), (0, operators_1.switchMap)(() => {
|
|
11
|
-
const URL =
|
|
10
|
+
const URL = `${zeniAPI.apiEndPoints.notificationMicroServiceBaseUrl}/1.0/notification-preferences`;
|
|
12
11
|
const { notificationSettings, notificationSettingsLocalData } = state$.value.settingsViewState.notification;
|
|
13
12
|
const payload = toSaveNotificationSettingsPayload(notificationSettingsLocalData, notificationSettings);
|
|
14
13
|
return zeniAPI
|
|
@@ -14,4 +14,4 @@ export declare const isMyBankConnectionsTabVisible: (currentTenant: CurrentTenan
|
|
|
14
14
|
export declare const isPerformanceTabVisible: (userRole: UserRoleType[], loggedInUser?: LoggedInUser) => boolean;
|
|
15
15
|
export declare const isRemindersSectionVisibleOnSettingsPage: (reminderNotificationSettings: ReminderNotificationSettingsData) => boolean;
|
|
16
16
|
export declare const isMonthEndNotificationsSectionVisibleOnSettingsPage: (monthEndInsightsNotificationSettings: MonthEndInsightsNotificationSettingsData, signedInUseEmail: string) => boolean;
|
|
17
|
-
export declare const isNotificationsTabVisible: (notificationSettings: NotificationSettingsData, signedInUseEmail: string, isDebitCardFlagEnabled: boolean, currentTenant: CurrentTenant | undefined
|
|
17
|
+
export declare const isNotificationsTabVisible: (notificationSettings: NotificationSettingsData, signedInUseEmail: string, isDebitCardFlagEnabled: boolean, currentTenant: CurrentTenant | undefined) => boolean;
|
|
@@ -160,14 +160,13 @@ const isMonthEndNotificationsSectionVisibleOnSettingsPage = (monthEndInsightsNot
|
|
|
160
160
|
monthEndInsightsNotificationSettings.recipientEmailIds.includes(signedInUseEmail));
|
|
161
161
|
};
|
|
162
162
|
exports.isMonthEndNotificationsSectionVisibleOnSettingsPage = isMonthEndNotificationsSectionVisibleOnSettingsPage;
|
|
163
|
-
const isNotificationsTabVisible = (notificationSettings, signedInUseEmail, isDebitCardFlagEnabled, currentTenant
|
|
163
|
+
const isNotificationsTabVisible = (notificationSettings, signedInUseEmail, isDebitCardFlagEnabled, currentTenant) => {
|
|
164
164
|
if (currentTenant == null) {
|
|
165
165
|
return false;
|
|
166
166
|
}
|
|
167
167
|
const { reminderNotificationSettings, monthEndInsightsNotificationSettings } = notificationSettings;
|
|
168
168
|
return (((0, exports.isRemindersSectionVisibleOnSettingsPage)(reminderNotificationSettings) ||
|
|
169
|
-
(0, exports.isMonthEndNotificationsSectionVisibleOnSettingsPage)(monthEndInsightsNotificationSettings, signedInUseEmail)
|
|
170
|
-
hasTaskChecklistAccess) &&
|
|
169
|
+
(0, exports.isMonthEndNotificationsSectionVisibleOnSettingsPage)(monthEndInsightsNotificationSettings, signedInUseEmail)) &&
|
|
171
170
|
((0, tenantSelector_1.isTenantAccountingServicesEnabled)(currentTenant) ||
|
|
172
171
|
((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
|
|
173
172
|
(0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false)));
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { AddressPayload } from '../../entity/address/addressPayload';
|
|
2
2
|
import { CompanyPayload } from '../../entity/company/companyPayload';
|
|
3
|
-
import { NotificationRegistryPayload } from '../../entity/notificationRegistry/notificationRegistryPayload';
|
|
4
3
|
import { UserPayload } from '../../entity/user/userPayload';
|
|
5
4
|
import { UserRolePayload } from '../../entity/userRole/userRolePayload';
|
|
6
5
|
import { ZeniAPIResponse } from '../../responsePayload';
|
|
7
6
|
import { AddressUpdatableInfoPayload } from '../addressView/addressViewPayload';
|
|
8
|
-
import { NotificationPreferencesPayload } from '../notificationPreferencesView/notificationPreferencesViewPayload';
|
|
9
7
|
interface MyProfilePeoplePayload {
|
|
10
8
|
user_roles: UserRolePayload[];
|
|
11
9
|
users: UserPayload[];
|
|
@@ -38,8 +36,6 @@ export interface NotificationSettingsPayload {
|
|
|
38
36
|
pending_reimbursement_approval_notification: RemindersPayload | null;
|
|
39
37
|
uncategorized_transaction_notification: RemindersPayload | null;
|
|
40
38
|
month_end_email_recipient_email_ids?: string[];
|
|
41
|
-
task_checklist_preferences?: NotificationPreferencesPayload;
|
|
42
|
-
task_checklist_registry?: NotificationRegistryPayload;
|
|
43
39
|
}
|
|
44
40
|
export type NotificationSettingsResponse = ZeniAPIResponse<NotificationSettingsPayload>;
|
|
45
41
|
interface RefreshQBOPayload {
|
|
@@ -12,7 +12,7 @@ export interface IntegrationsSettings {
|
|
|
12
12
|
export declare const REMINDER_NOTIFICATION_TYPES: readonly ["missingReceiptNotification", "uncategorizedTransactionNotification", "pendingBillApprovalNotification", "pendingReimbursementApprovalNotification"];
|
|
13
13
|
declare const toReminderTypeKey: (v: string) => "missingReceiptNotification" | "uncategorizedTransactionNotification" | "pendingBillApprovalNotification" | "pendingReimbursementApprovalNotification";
|
|
14
14
|
export type ReminderTypeKey = ReturnType<typeof toReminderTypeKey>;
|
|
15
|
-
export declare const toRemindersFrequencyType: (v: string) => "
|
|
15
|
+
export declare const toRemindersFrequencyType: (v: string) => "weekly" | "monthly" | "daily" | "bi-weekly";
|
|
16
16
|
export type FrequencyTypeKey = ReturnType<typeof toRemindersFrequencyType>;
|
|
17
17
|
export declare const NOTIFICATION_MODES: readonly ["email", "dashboard"];
|
|
18
18
|
export declare const toNotificationMode: (v: string) => "dashboard" | "email";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.1.57-
|
|
3
|
+
"version": "5.1.57-betaRD1",
|
|
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",
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { NotificationRegistry } from './notificationRegistryState';
|
|
2
|
-
export interface NotificationEventPayload {
|
|
3
|
-
channels: string[];
|
|
4
|
-
default_enabled: Record<string, boolean>;
|
|
5
|
-
display_name: string;
|
|
6
|
-
event_id: string;
|
|
7
|
-
suppressible: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface NotificationGroupPayload {
|
|
10
|
-
display_name: string;
|
|
11
|
-
events: NotificationEventPayload[];
|
|
12
|
-
group_id: string;
|
|
13
|
-
}
|
|
14
|
-
export interface NotificationRegistryPayload {
|
|
15
|
-
frequency_options: string[];
|
|
16
|
-
groups: NotificationGroupPayload[];
|
|
17
|
-
}
|
|
18
|
-
export declare const mapNotificationRegistryPayloadToRegistry: (payload: NotificationRegistryPayload) => NotificationRegistry;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mapNotificationRegistryPayloadToRegistry = void 0;
|
|
4
|
-
const notificationRegistryState_1 = require("./notificationRegistryState");
|
|
5
|
-
const mapDefaultEnabled = (raw) => {
|
|
6
|
-
const out = {};
|
|
7
|
-
Object.entries(raw).forEach(([channel, enabled]) => {
|
|
8
|
-
out[(0, notificationRegistryState_1.toNotificationChannel)(channel)] = enabled;
|
|
9
|
-
});
|
|
10
|
-
return out;
|
|
11
|
-
};
|
|
12
|
-
const mapEvent = (payload) => ({
|
|
13
|
-
channels: payload.channels.map(notificationRegistryState_1.toNotificationChannel),
|
|
14
|
-
defaultEnabled: mapDefaultEnabled(payload.default_enabled),
|
|
15
|
-
displayName: payload.display_name,
|
|
16
|
-
eventId: payload.event_id,
|
|
17
|
-
suppressible: payload.suppressible,
|
|
18
|
-
});
|
|
19
|
-
const mapGroup = (payload) => ({
|
|
20
|
-
displayName: payload.display_name,
|
|
21
|
-
events: payload.events.map(mapEvent),
|
|
22
|
-
groupId: payload.group_id,
|
|
23
|
-
});
|
|
24
|
-
const mapNotificationRegistryPayloadToRegistry = (payload) => ({
|
|
25
|
-
frequencyOptions: payload.frequency_options.map(notificationRegistryState_1.toNotificationFrequency),
|
|
26
|
-
groups: payload.groups.map(mapGroup),
|
|
27
|
-
});
|
|
28
|
-
exports.mapNotificationRegistryPayloadToRegistry = mapNotificationRegistryPayloadToRegistry;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { NotificationRegistryPayload } from './notificationRegistryPayload';
|
|
2
|
-
import { NotificationRegistryState } from './notificationRegistryState';
|
|
3
|
-
export declare const initialState: NotificationRegistryState;
|
|
4
|
-
export declare const clearAllNotificationRegistry: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"notificationRegistry/clearAllNotificationRegistry">, updateNotificationRegistry: import("@reduxjs/toolkit").ActionCreatorWithPayload<NotificationRegistryPayload, "notificationRegistry/updateNotificationRegistry">;
|
|
5
|
-
declare const _default: import("redux").Reducer<NotificationRegistryState>;
|
|
6
|
-
export default _default;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var _a;
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.updateNotificationRegistry = exports.clearAllNotificationRegistry = exports.initialState = void 0;
|
|
5
|
-
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
-
const notificationRegistryPayload_1 = require("./notificationRegistryPayload");
|
|
7
|
-
exports.initialState = {};
|
|
8
|
-
const notificationRegistry = (0, toolkit_1.createSlice)({
|
|
9
|
-
name: 'notificationRegistry',
|
|
10
|
-
initialState: exports.initialState,
|
|
11
|
-
reducers: {
|
|
12
|
-
clearAllNotificationRegistry() {
|
|
13
|
-
return exports.initialState;
|
|
14
|
-
},
|
|
15
|
-
updateNotificationRegistry(draft, action) {
|
|
16
|
-
// Full-replace contract: the registry payload is atomic — every fetch
|
|
17
|
-
// returns the complete group/event tree. If the backend ever ships a
|
|
18
|
-
// partial registry response, this write MUST switch to a merge (mirror
|
|
19
|
-
// the preferences slice's `mergePreferences`). Do not assume the
|
|
20
|
-
// current payload is partial without also updating this reducer.
|
|
21
|
-
draft.registry = (0, notificationRegistryPayload_1.mapNotificationRegistryPayloadToRegistry)(action.payload);
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
_a = notificationRegistry.actions, exports.clearAllNotificationRegistry = _a.clearAllNotificationRegistry, exports.updateNotificationRegistry = _a.updateNotificationRegistry;
|
|
26
|
-
exports.default = notificationRegistry.reducer;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getNotificationRegistry = void 0;
|
|
4
|
-
// Current behavior: a null / undefined registry means the whole notification
|
|
5
|
-
// section is hidden by consumers. This conflates two distinct states —
|
|
6
|
-
// "user has no saved preferences yet" vs "the Task Checklist feature gate is
|
|
7
|
-
// off for this tenant". Both currently surface as `undefined`. Distinguishing
|
|
8
|
-
// them (e.g. an explicit `gate_off` signal from the server) is a follow-up.
|
|
9
|
-
const getNotificationRegistry = (slice) => slice.registry;
|
|
10
|
-
exports.getNotificationRegistry = getNotificationRegistry;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare const NOTIFICATION_CHANNELS: readonly ["email", "slack", "in_app"];
|
|
2
|
-
export type NotificationChannel = (typeof NOTIFICATION_CHANNELS)[number];
|
|
3
|
-
export declare const toNotificationChannel: (raw: string) => NotificationChannel;
|
|
4
|
-
export declare const NOTIFICATION_FREQUENCIES: readonly ["instant", "daily"];
|
|
5
|
-
export type NotificationFrequency = (typeof NOTIFICATION_FREQUENCIES)[number];
|
|
6
|
-
export declare const toNotificationFrequency: (raw: string) => NotificationFrequency;
|
|
7
|
-
export interface RegistryNotificationEvent {
|
|
8
|
-
channels: NotificationChannel[];
|
|
9
|
-
defaultEnabled: Partial<Record<NotificationChannel, boolean>>;
|
|
10
|
-
displayName: string;
|
|
11
|
-
eventId: string;
|
|
12
|
-
suppressible: boolean;
|
|
13
|
-
}
|
|
14
|
-
export interface RegistryNotificationGroup {
|
|
15
|
-
displayName: string;
|
|
16
|
-
events: RegistryNotificationEvent[];
|
|
17
|
-
groupId: string;
|
|
18
|
-
}
|
|
19
|
-
export interface NotificationRegistry {
|
|
20
|
-
frequencyOptions: NotificationFrequency[];
|
|
21
|
-
groups: RegistryNotificationGroup[];
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* The registry is a singleton (not per-tenant). We keep the wrapper so
|
|
25
|
-
* Immer/RTK can safely represent "cleared" as `registry: undefined` — a bare
|
|
26
|
-
* top-level `undefined` state is not observable by Immer, which prevents
|
|
27
|
-
* `clearAllNotificationRegistry` from taking effect.
|
|
28
|
-
*/
|
|
29
|
-
export interface NotificationRegistryState {
|
|
30
|
-
registry?: NotificationRegistry;
|
|
31
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toNotificationFrequency = exports.NOTIFICATION_FREQUENCIES = exports.toNotificationChannel = exports.NOTIFICATION_CHANNELS = void 0;
|
|
4
|
-
const stringToUnion_1 = require("../../commonStateTypes/stringToUnion");
|
|
5
|
-
exports.NOTIFICATION_CHANNELS = ['email', 'slack', 'in_app'];
|
|
6
|
-
const toNotificationChannel = (raw) => (0, stringToUnion_1.stringToUnion)(raw, exports.NOTIFICATION_CHANNELS);
|
|
7
|
-
exports.toNotificationChannel = toNotificationChannel;
|
|
8
|
-
exports.NOTIFICATION_FREQUENCIES = ['instant', 'daily'];
|
|
9
|
-
const toNotificationFrequency = (raw) => (0, stringToUnion_1.stringToUnion)(raw, exports.NOTIFICATION_FREQUENCIES);
|
|
10
|
-
exports.toNotificationFrequency = toNotificationFrequency;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { toNotificationChannel, toNotificationFrequency, } from './notificationRegistryState';
|
|
2
|
-
const mapDefaultEnabled = (raw) => {
|
|
3
|
-
const out = {};
|
|
4
|
-
Object.entries(raw).forEach(([channel, enabled]) => {
|
|
5
|
-
out[toNotificationChannel(channel)] = enabled;
|
|
6
|
-
});
|
|
7
|
-
return out;
|
|
8
|
-
};
|
|
9
|
-
const mapEvent = (payload) => ({
|
|
10
|
-
channels: payload.channels.map(toNotificationChannel),
|
|
11
|
-
defaultEnabled: mapDefaultEnabled(payload.default_enabled),
|
|
12
|
-
displayName: payload.display_name,
|
|
13
|
-
eventId: payload.event_id,
|
|
14
|
-
suppressible: payload.suppressible,
|
|
15
|
-
});
|
|
16
|
-
const mapGroup = (payload) => ({
|
|
17
|
-
displayName: payload.display_name,
|
|
18
|
-
events: payload.events.map(mapEvent),
|
|
19
|
-
groupId: payload.group_id,
|
|
20
|
-
});
|
|
21
|
-
export const mapNotificationRegistryPayloadToRegistry = (payload) => ({
|
|
22
|
-
frequencyOptions: payload.frequency_options.map(toNotificationFrequency),
|
|
23
|
-
groups: payload.groups.map(mapGroup),
|
|
24
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
-
import { mapNotificationRegistryPayloadToRegistry, } from './notificationRegistryPayload';
|
|
3
|
-
export const initialState = {};
|
|
4
|
-
const notificationRegistry = createSlice({
|
|
5
|
-
name: 'notificationRegistry',
|
|
6
|
-
initialState,
|
|
7
|
-
reducers: {
|
|
8
|
-
clearAllNotificationRegistry() {
|
|
9
|
-
return initialState;
|
|
10
|
-
},
|
|
11
|
-
updateNotificationRegistry(draft, action) {
|
|
12
|
-
// Full-replace contract: the registry payload is atomic — every fetch
|
|
13
|
-
// returns the complete group/event tree. If the backend ever ships a
|
|
14
|
-
// partial registry response, this write MUST switch to a merge (mirror
|
|
15
|
-
// the preferences slice's `mergePreferences`). Do not assume the
|
|
16
|
-
// current payload is partial without also updating this reducer.
|
|
17
|
-
draft.registry = mapNotificationRegistryPayloadToRegistry(action.payload);
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
export const { clearAllNotificationRegistry, updateNotificationRegistry } = notificationRegistry.actions;
|
|
22
|
-
export default notificationRegistry.reducer;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// Current behavior: a null / undefined registry means the whole notification
|
|
2
|
-
// section is hidden by consumers. This conflates two distinct states —
|
|
3
|
-
// "user has no saved preferences yet" vs "the Task Checklist feature gate is
|
|
4
|
-
// off for this tenant". Both currently surface as `undefined`. Distinguishing
|
|
5
|
-
// them (e.g. an explicit `gate_off` signal from the server) is a follow-up.
|
|
6
|
-
export const getNotificationRegistry = (slice) => slice.registry;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { stringToUnion } from '../../commonStateTypes/stringToUnion';
|
|
2
|
-
export const NOTIFICATION_CHANNELS = ['email', 'slack', 'in_app'];
|
|
3
|
-
export const toNotificationChannel = (raw) => stringToUnion(raw, NOTIFICATION_CHANNELS);
|
|
4
|
-
export const NOTIFICATION_FREQUENCIES = ['instant', 'daily'];
|
|
5
|
-
export const toNotificationFrequency = (raw) => stringToUnion(raw, NOTIFICATION_FREQUENCIES);
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { EMPTY, concat, of } from 'rxjs';
|
|
2
|
-
import { catchError, debounceTime, filter, finalize, mergeMap, switchMap, withLatestFrom, } from 'rxjs/operators';
|
|
3
|
-
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
4
|
-
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
5
|
-
import { notificationPreferencesUrl } from '../notificationPreferencesEndpoint';
|
|
6
|
-
import { mapPreferencesToPayload, } from '../notificationPreferencesViewPayload';
|
|
7
|
-
import { saveNotificationPreferences, saveNotificationPreferencesFailure, saveNotificationPreferencesSuccess, setGroupFrequency, toggleEventChannel, } from '../notificationPreferencesViewReducer';
|
|
8
|
-
import { getNotificationLocalOverrides } from '../notificationPreferencesViewSelector';
|
|
9
|
-
const DEBOUNCE_MS = 300;
|
|
10
|
-
export const saveNotificationPreferencesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter((action) => toggleEventChannel.match(action) || setGroupFrequency.match(action)),
|
|
11
|
-
// Coalesce bursts of toggles into a single PUT. Trade-off: rapid clickers
|
|
12
|
-
// defer saves until they pause. Consider a max-wait wrapper if this
|
|
13
|
-
// becomes a UX problem.
|
|
14
|
-
debounceTime(DEBOUNCE_MS), withLatestFrom(state$),
|
|
15
|
-
// `switchMap` unsubscribes from any in-flight PUT observable when a new
|
|
16
|
-
// debounced batch fires. The prior HTTP request has already left the
|
|
17
|
-
// client — the network call is NOT cancelled — but its response is
|
|
18
|
-
// ignored, so stale success/failure actions cannot clobber the newer
|
|
19
|
-
// batch's state. Server-side ordering is arrival-order LWW.
|
|
20
|
-
switchMap(([, state]) => {
|
|
21
|
-
const overrides = getNotificationLocalOverrides(state);
|
|
22
|
-
const preferencesPatch = mapPreferencesToPayload(overrides);
|
|
23
|
-
if (Object.keys(preferencesPatch).length === 0) {
|
|
24
|
-
// No diff → do not dispatch a synthetic success (that would wipe
|
|
25
|
-
// stored preferences via the reducer). Simply no-op.
|
|
26
|
-
return EMPTY;
|
|
27
|
-
}
|
|
28
|
-
// Snapshot the Cancel epoch at PUT-dispatch time. Every Cancel bumps
|
|
29
|
-
// `cancelEpoch`; the reducer compares the captured value on Success/
|
|
30
|
-
// Failure and skips the merge when the value has advanced (user
|
|
31
|
-
// pressed Cancel since this save started). See Cursor Bugbot 4aa021f7
|
|
32
|
-
// — the earlier counter model failed when `switchMap` abandoned an
|
|
33
|
-
// in-flight save's response (Success/Failure never dispatched, so
|
|
34
|
-
// the counter never drained and future saves got their merge dropped).
|
|
35
|
-
const cancelEpochAtDispatch = state.notificationPreferencesViewState.cancelEpoch;
|
|
36
|
-
const body = { task_checklist_preferences: preferencesPatch };
|
|
37
|
-
// Suppress the error snackbar when the response lands AFTER the user
|
|
38
|
-
// pressed Cancel — the Failure reducer already swallows the state
|
|
39
|
-
// update, so the toast would be a stale error for a save the user
|
|
40
|
-
// abandoned. `StateObservable#value` reads the current cancelEpoch at
|
|
41
|
-
// response time (not at dispatch time — that's what
|
|
42
|
-
// `cancelEpochAtDispatch` captures). Cursor Bugbot da2f4fe2 + Nikunj.
|
|
43
|
-
const errorSnackbar = openSnackbar({
|
|
44
|
-
messageSection: 'notification_settings_saved',
|
|
45
|
-
messageText: 'failed',
|
|
46
|
-
type: 'error',
|
|
47
|
-
});
|
|
48
|
-
const isCancelledSince = () => cancelEpochAtDispatch <
|
|
49
|
-
state$.value.notificationPreferencesViewState.cancelEpoch;
|
|
50
|
-
// Abort the in-flight HTTP request when `switchMap` disposes this
|
|
51
|
-
// observable (new debounced batch fires, tenant switch, unmount).
|
|
52
|
-
// Without an AbortSignal, `switchMap` would drop the response
|
|
53
|
-
// client-side but the older PUT would still land on the server —
|
|
54
|
-
// under patch-semantic LWW, a slower older PUT arriving after a
|
|
55
|
-
// newer one silently reverts the user's latest choice on the
|
|
56
|
-
// server. Cursor Bugbot e3861a76. `AbortController.abort()` is
|
|
57
|
-
// idempotent so it's safe if the request already completed.
|
|
58
|
-
const controller = new AbortController();
|
|
59
|
-
const request$ = zeniAPI
|
|
60
|
-
.putAndGetJSON(notificationPreferencesUrl(zeniAPI), body, undefined, controller.signal)
|
|
61
|
-
.pipe(mergeMap((response) => {
|
|
62
|
-
if (isSuccessResponse(response)) {
|
|
63
|
-
return of(saveNotificationPreferencesSuccess({
|
|
64
|
-
cancelEpochAtDispatch,
|
|
65
|
-
savedOverrides: overrides,
|
|
66
|
-
serverEcho: response.data?.task_checklist_preferences,
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
const actions = [
|
|
70
|
-
saveNotificationPreferencesFailure({
|
|
71
|
-
cancelEpochAtDispatch,
|
|
72
|
-
error: response.status,
|
|
73
|
-
}),
|
|
74
|
-
];
|
|
75
|
-
if (!isCancelledSince()) {
|
|
76
|
-
// Surface a user-visible error — without it the UI reverts to
|
|
77
|
-
// server truth after debounce and the failed toggle looks like
|
|
78
|
-
// a mystery UI bug.
|
|
79
|
-
actions.push(errorSnackbar);
|
|
80
|
-
}
|
|
81
|
-
return of(...actions);
|
|
82
|
-
}), catchError((error) => {
|
|
83
|
-
const actions = [
|
|
84
|
-
saveNotificationPreferencesFailure({
|
|
85
|
-
cancelEpochAtDispatch,
|
|
86
|
-
error: createZeniAPIStatus('Unexpected Error', 'Save notification preferences failed: ' +
|
|
87
|
-
JSON.stringify(error)),
|
|
88
|
-
}),
|
|
89
|
-
];
|
|
90
|
-
if (!isCancelledSince()) {
|
|
91
|
-
actions.push(errorSnackbar);
|
|
92
|
-
}
|
|
93
|
-
return of(...actions);
|
|
94
|
-
}), finalize(() => {
|
|
95
|
-
// switchMap disposal (new batch fires, cancel, unmount) → abort
|
|
96
|
-
// the still-in-flight HTTP request. No-op if the request has
|
|
97
|
-
// already completed successfully or errored.
|
|
98
|
-
controller.abort();
|
|
99
|
-
}));
|
|
100
|
-
// Flip savePreferencesState to In-Progress BEFORE the network call so
|
|
101
|
-
// any "saving…" UI can render.
|
|
102
|
-
return concat(of(saveNotificationPreferences()), request$);
|
|
103
|
-
}));
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// Shared endpoint used by both the GET (envelope refresh in
|
|
2
|
-
// `fetchNotificationSettingsEpic`) and the PUT (patch in
|
|
3
|
-
// `saveNotificationPreferencesEpic`). Kept as a helper so an endpoint rename
|
|
4
|
-
// can't drift between the two callers.
|
|
5
|
-
export const notificationPreferencesUrl = (zeniAPI) => `${zeniAPI.apiEndPoints.notificationMicroServiceBaseUrl}/1.0/notification-preferences`;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { toNotificationChannel, toNotificationFrequency, } from '../../entity/notificationRegistry/notificationRegistryState';
|
|
2
|
-
const mapEventEnabledByChannel = (raw) => {
|
|
3
|
-
if (raw == null) {
|
|
4
|
-
return {};
|
|
5
|
-
}
|
|
6
|
-
const out = {};
|
|
7
|
-
Object.entries(raw).forEach(([eventId, channelMap]) => {
|
|
8
|
-
const mapped = {};
|
|
9
|
-
Object.entries(channelMap).forEach(([channel, enabled]) => {
|
|
10
|
-
mapped[toNotificationChannel(channel)] = enabled;
|
|
11
|
-
});
|
|
12
|
-
out[eventId] = mapped;
|
|
13
|
-
});
|
|
14
|
-
return out;
|
|
15
|
-
};
|
|
16
|
-
export const mapPayloadToPreferences = (payload) => {
|
|
17
|
-
const groupFrequencyRaw = payload?.group_frequency_by_group_id ?? {};
|
|
18
|
-
const groupFrequencyByGroupId = Object.fromEntries(Object.entries(groupFrequencyRaw).map(([groupId, frequency]) => [
|
|
19
|
-
groupId,
|
|
20
|
-
toNotificationFrequency(frequency),
|
|
21
|
-
]));
|
|
22
|
-
return {
|
|
23
|
-
eventEnabledByChannel: mapEventEnabledByChannel(payload?.event_enabled_by_channel),
|
|
24
|
-
groupFrequencyByGroupId,
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
export const mapPreferencesToPayload = (preferences) => {
|
|
28
|
-
const payload = {};
|
|
29
|
-
if (Object.keys(preferences.eventEnabledByChannel).length > 0) {
|
|
30
|
-
payload.event_enabled_by_channel = preferences.eventEnabledByChannel;
|
|
31
|
-
}
|
|
32
|
-
if (Object.keys(preferences.groupFrequencyByGroupId).length > 0) {
|
|
33
|
-
payload.group_frequency_by_group_id = preferences.groupFrequencyByGroupId;
|
|
34
|
-
}
|
|
35
|
-
return payload;
|
|
36
|
-
};
|