@zeniai/client-epic-state 5.2.49-beta0ND → 5.2.49-betaAK0
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/notificationRegistry/notificationRegistryReducer.d.ts +1 -2
- package/lib/entity/notificationRegistry/notificationRegistryReducer.js +9 -20
- package/lib/entity/notificationRegistry/notificationRegistrySelector.d.ts +1 -2
- package/lib/entity/notificationRegistry/notificationRegistrySelector.js +1 -7
- package/lib/entity/notificationRegistry/notificationRegistryState.d.ts +0 -5
- package/lib/entity/notificationRegistry/notificationRegistryState.js +1 -7
- package/lib/entity/sectionProjectView/sectionProjectViewReducer.js +1 -3
- package/lib/epic.js +4 -4
- package/lib/esm/entity/notificationRegistry/notificationRegistryReducer.js +9 -20
- package/lib/esm/entity/notificationRegistry/notificationRegistrySelector.js +0 -5
- package/lib/esm/entity/notificationRegistry/notificationRegistryState.js +0 -5
- package/lib/esm/entity/sectionProjectView/sectionProjectViewReducer.js +1 -3
- package/lib/esm/epic.js +4 -4
- package/lib/esm/index.js +4 -4
- package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +2 -4
- package/lib/esm/view/aiCfoView/epics/fetchRoutineRunsEpic.js +1 -3
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +4 -1
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -2
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +18 -9
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +3 -1
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +16 -7
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +4 -2
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +21 -13
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +3 -2
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +18 -9
- package/lib/esm/view/invoicing/invoicingFormDraft.js +56 -0
- package/lib/esm/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +7 -20
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewReducer.js +17 -43
- package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewSelector.js +4 -24
- package/lib/esm/view/settingsView/epic/fetchNotificationSettingsEpic.js +2 -12
- package/lib/index.d.ts +4 -4
- package/lib/index.js +14 -21
- package/lib/view/aiCfoView/aiCfoViewReducer.js +2 -4
- package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +1 -1
- package/lib/view/aiCfoView/epics/fetchRoutineRunsEpic.js +1 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +10 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +4 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +17 -8
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.d.ts +13 -0
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +3 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +15 -6
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +9 -0
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +4 -2
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +21 -13
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +9 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +3 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +17 -8
- package/lib/view/invoicing/invoicingFormDraft.d.ts +50 -0
- package/lib/view/invoicing/invoicingFormDraft.js +63 -0
- package/lib/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +6 -19
- package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.d.ts +5 -8
- package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.js +17 -43
- package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.d.ts +0 -4
- package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.js +5 -30
- package/lib/view/notificationPreferencesView/notificationPreferencesViewState.d.ts +3 -4
- package/lib/view/settingsView/epic/fetchNotificationSettingsEpic.js +2 -12
- package/lib/view/settingsView/settingsViewPayload.d.ts +0 -2
- package/package.json +1 -1
package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js
CHANGED
|
@@ -5,6 +5,7 @@ const reduceFetchState_1 = require("../../../commonStateTypes/reduceFetchState")
|
|
|
5
5
|
const couponSelector_1 = require("../../../entity/invoicing/coupon/couponSelector");
|
|
6
6
|
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
7
7
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
8
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
8
9
|
const invoicingQboViewSelector_1 = require("../invoicingQboView/invoicingQboViewSelector");
|
|
9
10
|
const settingsViewSelector_1 = require("../settingsView/settingsViewSelector");
|
|
10
11
|
const editInvoicingCouponDetailViewState_1 = require("./editInvoicingCouponDetailViewState");
|
|
@@ -44,6 +45,7 @@ const getEditInvoicingCouponDetailViewState = (state, couponId) => {
|
|
|
44
45
|
{ error: undefined, fetchState: configFetchState },
|
|
45
46
|
]),
|
|
46
47
|
recordState,
|
|
48
|
+
isFormLoading: (0, invoicingFormDraft_1.isInvoicingFormDraftPending)(state.editInvoicingCouponDetailViewState.editDraftById, couponId),
|
|
47
49
|
coupon,
|
|
48
50
|
discountsAccountName: (0, invoicingQboViewSelector_1.invoicingQboAccountByQboId)(state, coupon?.discountsAccountId)?.accountName,
|
|
49
51
|
savedCouponId,
|
|
@@ -74,7 +76,7 @@ const getInvoicingCouponFormView = (state, couponId) => {
|
|
|
74
76
|
// `seedInvoicingCouponFormDraftEpic` when the detail load lands. Until then
|
|
75
77
|
// the page is showing its skeleton.
|
|
76
78
|
const localData = couponId != null
|
|
77
|
-
? (editDraftById
|
|
79
|
+
? (0, invoicingFormDraft_1.getInvoicingFormDraftLocalData)(editDraftById, couponId, editInvoicingCouponDetailViewState_1.initialInvoicingCouponFormLocalData)
|
|
78
80
|
: newCouponDraft;
|
|
79
81
|
const { billingPeriodUnitOptions, discountApplyOnOptions, discountDurationTypeOptions, discountTypeOptions, qboAccountMappingKindOptions, } = (0, invoicingConfigViewSelector_1.getInvoicingConfigView)(state);
|
|
80
82
|
const isQboConnected = (0, invoicingQboViewSelector_1.isInvoicingQboConnected)(state);
|
package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Amount } from '../../../commonStateTypes/amount';
|
|
|
2
2
|
import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
|
|
3
3
|
import { InvoicingCoupon } from '../../../entity/invoicing/coupon/couponState';
|
|
4
4
|
import { InvoicingQboAccountMappingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
5
|
+
import { InvoicingFormDraftById } from '../invoicingFormDraft';
|
|
5
6
|
/**
|
|
6
7
|
* Draft form state for the create/edit discount-coupon form. Held in CES so the
|
|
7
8
|
* form flows its data from here (mirrors the customer form pattern). Dates are
|
|
@@ -37,7 +38,7 @@ export interface InvoicingCouponFormLocalData {
|
|
|
37
38
|
export interface EditInvoicingCouponDetailViewState extends FetchedState {
|
|
38
39
|
/** Lifecycle action (archive, activate, …) fetch state per coupon id. */
|
|
39
40
|
actionStateById: Record<ID, FetchStateAndError>;
|
|
40
|
-
editDraftById:
|
|
41
|
+
editDraftById: InvoicingFormDraftById<InvoicingCouponFormLocalData>;
|
|
41
42
|
newCouponDraft: InvoicingCouponFormLocalData;
|
|
42
43
|
/** Detail-load state per coupon id. */
|
|
43
44
|
recordStateById: Record<ID, FetchStateAndError>;
|
|
@@ -23,7 +23,7 @@ const saveInvoicingCouponEpic = (actions$, state$, zeniAPI) => actions$.pipe((0,
|
|
|
23
23
|
const state = state$.value;
|
|
24
24
|
const viewState = state.editInvoicingCouponDetailViewState;
|
|
25
25
|
const draft = (id != null
|
|
26
|
-
? viewState.editDraftById[id]
|
|
26
|
+
? viewState.editDraftById[id]?.localData
|
|
27
27
|
: viewState.newCouponDraft) ?? viewState.newCouponDraft;
|
|
28
28
|
// The create default is resolved here as well as in the form view,
|
|
29
29
|
// so a create that never touched the picker still persists the
|
package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { RootState } from '../../../rootStateTypes';
|
|
4
|
-
import { seedInvoicingCouponFormDraft, updateInvoicingCouponDetail } from './editInvoicingCouponDetailViewReducer';
|
|
5
|
-
export type ActionType = ReturnType<typeof updateInvoicingCouponDetail> | ReturnType<typeof seedInvoicingCouponFormDraft>;
|
|
4
|
+
import { fetchInvoicingCouponDetailPage, seedInvoicingCouponFormDraft, updateInvoicingCouponDetail } from './editInvoicingCouponDetailViewReducer';
|
|
5
|
+
export type ActionType = ReturnType<typeof fetchInvoicingCouponDetailPage> | ReturnType<typeof updateInvoicingCouponDetail> | ReturnType<typeof seedInvoicingCouponFormDraft>;
|
|
6
6
|
/**
|
|
7
|
-
* Seeds the edit draft from the record the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
8
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
9
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
10
|
+
*
|
|
11
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
12
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
13
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
14
|
+
* or a cancel) would have nothing left to seed it.
|
|
11
15
|
*/
|
|
12
16
|
export declare const seedInvoicingCouponFormDraftEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;
|
package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js
CHANGED
|
@@ -7,14 +7,23 @@ const couponSelector_1 = require("../../../entity/invoicing/coupon/couponSelecto
|
|
|
7
7
|
const editInvoicingCouponDetailViewReducer_1 = require("./editInvoicingCouponDetailViewReducer");
|
|
8
8
|
const editInvoicingCouponDetailViewState_1 = require("./editInvoicingCouponDetailViewState");
|
|
9
9
|
/**
|
|
10
|
-
* Seeds the edit draft from the record the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
11
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
12
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
13
|
+
*
|
|
14
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
15
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
16
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
17
|
+
* or a cancel) would have nothing left to seed it.
|
|
14
18
|
*/
|
|
15
|
-
const seedInvoicingCouponFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)(editInvoicingCouponDetailViewReducer_1.updateInvoicingCouponDetail.match
|
|
19
|
+
const seedInvoicingCouponFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)((action) => editInvoicingCouponDetailViewReducer_1.updateInvoicingCouponDetail.match(action) ||
|
|
20
|
+
editInvoicingCouponDetailViewReducer_1.fetchInvoicingCouponDetailPage.match(action)), (0, operators_1.withLatestFrom)(state$), (0, operators_1.mergeMap)(([action, state]) => {
|
|
16
21
|
const { id } = action.payload;
|
|
17
|
-
|
|
22
|
+
// Nothing to seed from on a cold open; the detail response triggers this
|
|
23
|
+
// again once it lands.
|
|
24
|
+
const coupon = id != null
|
|
25
|
+
? (0, couponSelector_1.getInvoicingCouponById)(state.invoicingCouponState, id)
|
|
26
|
+
: undefined;
|
|
18
27
|
if (coupon == null) {
|
|
19
28
|
return rxjs_1.EMPTY;
|
|
20
29
|
}
|
|
@@ -54,6 +54,8 @@ export declare const fetchInvoicingPaymentLinkEmailPreview: import("@reduxjs/too
|
|
|
54
54
|
status: ZeniAPIStatus;
|
|
55
55
|
}, "editInvoicingCustomerDetailView/updateInvoicingCustomerDetailFailure">, updateInvoicingCustomerFormDraft: import("@reduxjs/toolkit").ActionCreatorWithPayload<UpdateInvoicingCustomerFormDraftPayload, "editInvoicingCustomerDetailView/updateInvoicingCustomerFormDraft">, submitInvoicingCustomerForm: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SubmitInvoicingCustomerFormPayload], SubmitInvoicingCustomerFormPayload, "editInvoicingCustomerDetailView/submitInvoicingCustomerForm", never, never>, submitInvoicingCustomerFormSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
56
56
|
customerId: ID;
|
|
57
|
-
}, "editInvoicingCustomerDetailView/submitInvoicingCustomerFormSuccess">, submitInvoicingCustomerFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingCustomerDetailView/submitInvoicingCustomerFormFailure">, resetEditInvoicingCustomerDetailView: import("@reduxjs/toolkit").
|
|
57
|
+
}, "editInvoicingCustomerDetailView/submitInvoicingCustomerFormSuccess">, submitInvoicingCustomerFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingCustomerDetailView/submitInvoicingCustomerFormFailure">, resetEditInvoicingCustomerDetailView: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
58
|
+
id?: ID;
|
|
59
|
+
} | undefined, "editInvoicingCustomerDetailView/resetEditInvoicingCustomerDetailView">, initializeInvoicingCustomerAddress: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: InitializeInvoicingCustomerAddressPayload], InitializeInvoicingCustomerAddressPayload, "editInvoicingCustomerDetailView/initializeInvoicingCustomerAddress", never, never>;
|
|
58
60
|
declare const _default: import("redux").Reducer<EditInvoicingCustomerDetailViewState>;
|
|
59
61
|
export default _default;
|
package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.initializeInvoicingCustomerAddress = exports.resetEditInvoicingCustomerDetailView = exports.submitInvoicingCustomerFormFailure = exports.submitInvoicingCustomerFormSuccess = exports.submitInvoicingCustomerForm = exports.updateInvoicingCustomerFormDraft = exports.updateInvoicingCustomerDetailFailure = exports.submitAddPromotionalCredits = exports.resetPromotionalCreditAction = exports.updateInvoicingCustomerDetail = exports.seedInvoicingCustomerFormDraft = exports.fetchInvoicingCustomerDetailPage = exports.fetchInvoicingCustomerDetail = exports.addPromotionalCreditsSuccess = exports.addPromotionalCreditsFailure = exports.updateInvoicingPaymentLinkEmailPreviewFailure = exports.updateInvoicingPaymentLinkEmailPreview = exports.fetchInvoicingPaymentLinkEmailPreviewPage = exports.fetchInvoicingPaymentLinkEmailPreview = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const rootActions_1 = require("../../../rootActions");
|
|
7
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
7
8
|
const invoicingCustomerFormConfig_1 = require("./invoicingCustomerFormConfig");
|
|
8
9
|
exports.initialState = {
|
|
9
10
|
emailPreviewFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
@@ -127,20 +128,17 @@ const editInvoicingCustomerDetailView = (0, toolkit_1.createSlice)({
|
|
|
127
128
|
fetchState: 'Error',
|
|
128
129
|
error: action.payload.status,
|
|
129
130
|
};
|
|
131
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftFailure)(draft.editDraftById, action.payload.id, invoicingCustomerFormConfig_1.initialInvoicingCustomerFormLocalData);
|
|
130
132
|
},
|
|
131
|
-
/**
|
|
132
|
-
* Seed the draft from the loaded record, once. Skipped when a draft already
|
|
133
|
-
* exists so a late refetch cannot overwrite in-progress edits.
|
|
134
|
-
*/
|
|
133
|
+
/** Fill the draft from the loaded record — see `applyInvoicingFormDraftSeed`. */
|
|
135
134
|
seedInvoicingCustomerFormDraft(draft, action) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
135
|
+
const { id, localData } = action.payload;
|
|
136
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftSeed)(draft.editDraftById, id, localData);
|
|
139
137
|
},
|
|
140
138
|
updateInvoicingCustomerFormDraft(draft, action) {
|
|
141
139
|
const { id, localData } = action.payload;
|
|
142
140
|
if (id != null) {
|
|
143
|
-
draft.editDraftById
|
|
141
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftEdit)(draft.editDraftById, id, localData);
|
|
144
142
|
}
|
|
145
143
|
else {
|
|
146
144
|
draft.newCustomerDraft = localData;
|
|
@@ -165,12 +163,22 @@ const editInvoicingCustomerDetailView = (0, toolkit_1.createSlice)({
|
|
|
165
163
|
draft.fetchState = 'Error';
|
|
166
164
|
draft.error = action.payload;
|
|
167
165
|
},
|
|
168
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Discard a form's draft — the customer's own when `id` is given,
|
|
168
|
+
* the create draft otherwise. Scoped to the one form on purpose: another
|
|
169
|
+
* customer's half-finished edit is not this form's to throw away.
|
|
170
|
+
* Clearing every draft at once is what `clearAll` is for.
|
|
171
|
+
*/
|
|
172
|
+
resetEditInvoicingCustomerDetailView(draft, action) {
|
|
169
173
|
draft.fetchState = 'Not-Started';
|
|
170
174
|
draft.error = undefined;
|
|
171
175
|
draft.savedCustomerId = undefined;
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
const id = action.payload?.id;
|
|
177
|
+
if (id == null) {
|
|
178
|
+
draft.newCustomerDraft = { ...invoicingCustomerFormConfig_1.initialInvoicingCustomerFormLocalData };
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
delete draft.editDraftById[id];
|
|
174
182
|
},
|
|
175
183
|
// Trigger only — handled by initializeInvoicingCustomerAddressEpic.
|
|
176
184
|
initializeInvoicingCustomerAddress: {
|
|
@@ -53,6 +53,15 @@ export interface InvoicingCustomerFormView extends SelectorView {
|
|
|
53
53
|
* Folded in here so the screen never reaches for the config view itself.
|
|
54
54
|
*/
|
|
55
55
|
fieldHelp: InvoicingFieldHelpByCode;
|
|
56
|
+
/**
|
|
57
|
+
* True while the customer being edited is still on its way to the form. The
|
|
58
|
+
* form holds a skeleton on this rather than mount: it seeds react-hook-form
|
|
59
|
+
* once, at first render, so fields mounted against the blank draft stay blank
|
|
60
|
+
* and the first change writes that blankness back over the customer. A later
|
|
61
|
+
* refetch does not raise this again, so a refresh leaves the form on screen
|
|
62
|
+
* instead of blanking it mid-edit.
|
|
63
|
+
*/
|
|
64
|
+
isFormLoading: boolean;
|
|
56
65
|
localData: InvoicingCustomerFormLocalData;
|
|
57
66
|
mode: 'create' | 'edit';
|
|
58
67
|
netTermDayOptions: InvoicingNamedOption[];
|
|
@@ -8,6 +8,7 @@ const invoicingCustomerSelector_2 = require("../../../entity/invoicing/invoicing
|
|
|
8
8
|
const addressViewSelector_1 = require("../../addressView/addressViewSelector");
|
|
9
9
|
const customerPaymentMethodSelector_1 = require("../customerPaymentMethod/customerPaymentMethodSelector");
|
|
10
10
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
11
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
11
12
|
const invoicingSettingsHelpers_1 = require("../settingsView/invoicingSettingsHelpers");
|
|
12
13
|
const settingsViewSelector_1 = require("../settingsView/settingsViewSelector");
|
|
13
14
|
const invoicingCustomerFormConfig_1 = require("./invoicingCustomerFormConfig");
|
|
@@ -63,8 +64,7 @@ const getInvoicingCustomerFormView = (state, invoicingCustomerID) => {
|
|
|
63
64
|
// `seedInvoicingCustomerFormDraftEpic` when the detail load lands. Until then
|
|
64
65
|
// the page is showing its skeleton.
|
|
65
66
|
const localData = invoicingCustomerID != null
|
|
66
|
-
? (editDraftById
|
|
67
|
-
invoicingCustomerFormConfig_1.initialInvoicingCustomerFormLocalData)
|
|
67
|
+
? (0, invoicingFormDraft_1.getInvoicingFormDraftLocalData)(editDraftById, invoicingCustomerID, invoicingCustomerFormConfig_1.initialInvoicingCustomerFormLocalData)
|
|
68
68
|
: newCustomerDraft;
|
|
69
69
|
// Billing address is captured via the shared Address screen and seeded there
|
|
70
70
|
// (from the address entity) on detail load, so the formatted label comes from
|
|
@@ -82,6 +82,7 @@ const getInvoicingCustomerFormView = (state, invoicingCustomerID) => {
|
|
|
82
82
|
? { error: undefined, fetchState: 'Completed' }
|
|
83
83
|
: (state.editInvoicingCustomerDetailViewState.recordStateById[invoicingCustomerID] ?? { error: undefined, fetchState: 'Not-Started' });
|
|
84
84
|
const { emailPreview, emailPreviewCustomerId, emailPreviewFetchState } = state.editInvoicingCustomerDetailViewState;
|
|
85
|
+
const isFormLoading = (0, invoicingFormDraft_1.isInvoicingFormDraftPending)(editDraftById, invoicingCustomerID);
|
|
85
86
|
return {
|
|
86
87
|
// A preview cached for another customer is not this screen's.
|
|
87
88
|
emailPreview: emailPreviewCustomerId === invoicingCustomerID ? emailPreview : undefined,
|
|
@@ -96,6 +97,7 @@ const getInvoicingCustomerFormView = (state, invoicingCustomerID) => {
|
|
|
96
97
|
promotionalCreditSubmitState: state.editInvoicingCustomerDetailViewState.promotionalCreditSubmitState,
|
|
97
98
|
record: (0, exports.getInvoicingCustomerRecord)(state, invoicingCustomerID),
|
|
98
99
|
fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'customer'),
|
|
100
|
+
isFormLoading,
|
|
99
101
|
customerTypeOptions,
|
|
100
102
|
localData,
|
|
101
103
|
mode,
|
package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
|
|
2
|
+
import { InvoicingFormDraftById } from '../invoicingFormDraft';
|
|
2
3
|
/**
|
|
3
4
|
* The payment-link email the composer opens with: the backend's prefill plus
|
|
4
5
|
* the tokens the frontend splices the user's edits into. State, not wire shape
|
|
@@ -37,7 +38,7 @@ export interface InvoicingCustomerFormLocalData {
|
|
|
37
38
|
vatNumber: string;
|
|
38
39
|
}
|
|
39
40
|
export interface EditInvoicingCustomerDetailViewState extends FetchedState {
|
|
40
|
-
editDraftById:
|
|
41
|
+
editDraftById: InvoicingFormDraftById<InvoicingCustomerFormLocalData>;
|
|
41
42
|
/**
|
|
42
43
|
* The payment-link email rendered by the backend, cached per customer so
|
|
43
44
|
* reopening the composer costs nothing. `emailPreviewCustomerId` is the cache
|
|
@@ -19,7 +19,7 @@ const saveInvoicingCustomerEpic = (actions$, state$, zeniAPI) => actions$.pipe((
|
|
|
19
19
|
const rootState = state$.value;
|
|
20
20
|
const viewState = rootState.editInvoicingCustomerDetailViewState;
|
|
21
21
|
const localData = (id != null
|
|
22
|
-
? viewState.editDraftById[id]
|
|
22
|
+
? viewState.editDraftById[id]?.localData
|
|
23
23
|
: viewState.newCustomerDraft) ?? viewState.newCustomerDraft;
|
|
24
24
|
// Billing address captured via the shared Address screen is sent as an
|
|
25
25
|
// object in the payload; the backend creates it and returns its id
|
|
@@ -55,6 +55,8 @@ export declare const runInvoicingSubscriptionAction: import("@reduxjs/toolkit").
|
|
|
55
55
|
id: ID;
|
|
56
56
|
}, "editInvoicingSubscriptionDetailView/updateInvoicingSubscriptionDetailFailure">, updateInvoicingSubscriptionFormDraft: import("@reduxjs/toolkit").ActionCreatorWithPayload<UpdateInvoicingSubscriptionFormDraftPayload, "editInvoicingSubscriptionDetailView/updateInvoicingSubscriptionFormDraft">, submitInvoicingSubscriptionForm: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SubmitInvoicingSubscriptionFormPayload], SubmitInvoicingSubscriptionFormPayload, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionForm", never, never>, submitInvoicingSubscriptionFormSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
57
57
|
subscriptionId: ID;
|
|
58
|
-
}, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormSuccess">, submitInvoicingSubscriptionFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormFailure">, resetEditInvoicingSubscriptionDetailView: import("@reduxjs/toolkit").
|
|
58
|
+
}, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormSuccess">, submitInvoicingSubscriptionFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormFailure">, resetEditInvoicingSubscriptionDetailView: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
59
|
+
id?: ID;
|
|
60
|
+
} | undefined, "editInvoicingSubscriptionDetailView/resetEditInvoicingSubscriptionDetailView">, initializeInvoicingSubscriptionAddress: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: InitializeInvoicingSubscriptionAddressPayload], InitializeInvoicingSubscriptionAddressPayload, "editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddress", never, never>, syncInvoicingSubscriptionCoupons: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SyncInvoicingSubscriptionCouponsPayload], SyncInvoicingSubscriptionCouponsPayload, "editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCoupons", never, never>;
|
|
59
61
|
declare const _default: import("redux").Reducer<EditInvoicingSubscriptionDetailViewState>;
|
|
60
62
|
export default _default;
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.syncInvoicingSubscriptionCoupons = exports.initializeInvoicingSubscriptionAddress = exports.resetEditInvoicingSubscriptionDetailView = exports.submitInvoicingSubscriptionFormFailure = exports.submitInvoicingSubscriptionFormSuccess = exports.submitInvoicingSubscriptionForm = exports.updateInvoicingSubscriptionFormDraft = exports.updateInvoicingSubscriptionDetailFailure = exports.updateInvoicingSubscriptionDetail = exports.seedInvoicingSubscriptionFormDraft = exports.fetchInvoicingSubscriptionFormPage = exports.fetchInvoicingSubscriptionDetailPage = exports.fetchInvoicingSubscriptionDetail = exports.runInvoicingSubscriptionActionSuccess = exports.runInvoicingSubscriptionActionFailure = exports.runInvoicingSubscriptionAction = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const rootActions_1 = require("../../../rootActions");
|
|
7
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
7
8
|
const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
|
|
8
9
|
exports.initialState = {
|
|
9
10
|
fetchState: 'Not-Started',
|
|
@@ -69,15 +70,12 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
|
|
|
69
70
|
fetchState: 'Error',
|
|
70
71
|
error: action.payload.error,
|
|
71
72
|
};
|
|
73
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftFailure)(draft.editDraftById, action.payload.id, invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData);
|
|
72
74
|
},
|
|
73
|
-
/**
|
|
74
|
-
* Seed the draft from the loaded record, once. Skipped when a draft already
|
|
75
|
-
* exists so a late refetch cannot overwrite in-progress edits.
|
|
76
|
-
*/
|
|
75
|
+
/** Fill the draft from the loaded record — see `applyInvoicingFormDraftSeed`. */
|
|
77
76
|
seedInvoicingSubscriptionFormDraft(draft, action) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
77
|
+
const { id, localData } = action.payload;
|
|
78
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftSeed)(draft.editDraftById, id, localData);
|
|
81
79
|
},
|
|
82
80
|
fetchInvoicingSubscriptionFormPage: {
|
|
83
81
|
reducer() {
|
|
@@ -91,7 +89,7 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
|
|
|
91
89
|
updateInvoicingSubscriptionFormDraft(draft, action) {
|
|
92
90
|
const { id, localData } = action.payload;
|
|
93
91
|
if (id != null) {
|
|
94
|
-
draft.editDraftById
|
|
92
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftEdit)(draft.editDraftById, id, localData);
|
|
95
93
|
}
|
|
96
94
|
else {
|
|
97
95
|
draft.newSubscriptionDraft = localData;
|
|
@@ -116,14 +114,24 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
|
|
|
116
114
|
draft.fetchState = 'Error';
|
|
117
115
|
draft.error = action.payload;
|
|
118
116
|
},
|
|
119
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Discard a form's draft — the subscription's own when `id` is given,
|
|
119
|
+
* the create draft otherwise. Scoped to the one form on purpose: another
|
|
120
|
+
* subscription's half-finished edit is not this form's to throw away.
|
|
121
|
+
* Clearing every draft at once is what `clearAll` is for.
|
|
122
|
+
*/
|
|
123
|
+
resetEditInvoicingSubscriptionDetailView(draft, action) {
|
|
120
124
|
draft.fetchState = 'Not-Started';
|
|
121
125
|
draft.error = undefined;
|
|
122
126
|
draft.savedSubscriptionId = undefined;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const id = action.payload?.id;
|
|
128
|
+
if (id == null) {
|
|
129
|
+
draft.newSubscriptionDraft = {
|
|
130
|
+
...invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData,
|
|
131
|
+
};
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
delete draft.editDraftById[id];
|
|
127
135
|
},
|
|
128
136
|
// Trigger only — handled by initializeInvoicingSubscriptionAddressEpic.
|
|
129
137
|
initializeInvoicingSubscriptionAddress: {
|
|
@@ -41,6 +41,15 @@ export interface InvoicingSubscriptionFormView extends SelectorView {
|
|
|
41
41
|
* Folded in here so the screen never reaches for the config view itself.
|
|
42
42
|
*/
|
|
43
43
|
fieldHelp: InvoicingFieldHelpByCode;
|
|
44
|
+
/**
|
|
45
|
+
* True while the subscription being edited is still on its way to the form.
|
|
46
|
+
* The form holds a skeleton on this rather than mount: it seeds
|
|
47
|
+
* react-hook-form once, at first render, so fields mounted against the blank
|
|
48
|
+
* draft stay blank and the first change writes that blankness back over the
|
|
49
|
+
* subscription. A later refetch does not raise this again, so a refresh
|
|
50
|
+
* leaves the form on screen instead of blanking it mid-edit.
|
|
51
|
+
*/
|
|
52
|
+
isFormLoading: boolean;
|
|
44
53
|
localData: InvoicingSubscriptionFormLocalData;
|
|
45
54
|
mode: 'create' | 'edit';
|
|
46
55
|
/**
|
|
@@ -8,6 +8,7 @@ const couponViewSelector_1 = require("../couponView/couponViewSelector");
|
|
|
8
8
|
const invoicingCatalogListViewSelector_1 = require("../invoicingCatalogListView/invoicingCatalogListViewSelector");
|
|
9
9
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
10
10
|
const invoicingCustomerListViewSelector_1 = require("../invoicingCustomerListView/invoicingCustomerListViewSelector");
|
|
11
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
11
12
|
const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
|
|
12
13
|
const invoicingSubscriptionFormConfig_2 = require("./invoicingSubscriptionFormConfig");
|
|
13
14
|
const getEditInvoicingSubscriptionDetailViewState = (state, invoicingSubscriptionID) => {
|
|
@@ -37,8 +38,7 @@ const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
|
|
|
37
38
|
// `seedInvoicingSubscriptionFormDraftEpic` when the detail load lands. Until
|
|
38
39
|
// then the page is showing its skeleton.
|
|
39
40
|
const localData = invoicingSubscriptionID != null
|
|
40
|
-
? (editDraftById
|
|
41
|
-
invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData)
|
|
41
|
+
? (0, invoicingFormDraft_1.getInvoicingFormDraftLocalData)(editDraftById, invoicingSubscriptionID, invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData)
|
|
42
42
|
: newSubscriptionDraft;
|
|
43
43
|
// Shipping address is captured via the shared Address screen and seeded there
|
|
44
44
|
// (looked up in the shared address store by `shippingAddressId`) on detail
|
|
@@ -69,6 +69,7 @@ const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
|
|
|
69
69
|
subscriptionLifecycleActionOptions: configState.subscriptionLifecycleActionOptions,
|
|
70
70
|
subscriptionMetadataEditAllowedFromStatuses: configState.subscriptionMetadataEditAllowedFromStatuses,
|
|
71
71
|
recordState,
|
|
72
|
+
isFormLoading: (0, invoicingFormDraft_1.isInvoicingFormDraftPending)(editDraftById, invoicingSubscriptionID),
|
|
72
73
|
fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'subscription'),
|
|
73
74
|
coupons: (0, couponViewSelector_1.getInvoicingCouponListItems)(state),
|
|
74
75
|
customers: (0, invoicingCustomerListViewSelector_1.getInvoicingCustomerListView)(state).customers,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Amount } from '../../../commonStateTypes/amount';
|
|
2
2
|
import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
|
|
3
|
+
import { InvoicingFormDraftById } from '../invoicingFormDraft';
|
|
3
4
|
/**
|
|
4
5
|
* Draft form state for the create/edit subscription form. Held in CES so the
|
|
5
6
|
* form flows its data from here (mirrors the customer form / bill-pay pattern).
|
|
@@ -41,7 +42,7 @@ export interface InvoicingSubscriptionFormLocalData {
|
|
|
41
42
|
export interface EditInvoicingSubscriptionDetailViewState extends FetchedState {
|
|
42
43
|
/** Lifecycle action fetch state per subscription id. */
|
|
43
44
|
actionStateById: Record<ID, FetchStateAndError>;
|
|
44
|
-
editDraftById:
|
|
45
|
+
editDraftById: InvoicingFormDraftById<InvoicingSubscriptionFormLocalData>;
|
|
45
46
|
newSubscriptionDraft: InvoicingSubscriptionFormLocalData;
|
|
46
47
|
/** Detail-load state per subscription id. */
|
|
47
48
|
recordStateById: Record<ID, FetchStateAndError>;
|
|
@@ -19,8 +19,11 @@ const fetchInvoicingSubscriptionFormPageEpic = (actions$) => actions$.pipe((0, o
|
|
|
19
19
|
const { subscriptionId } = action.payload;
|
|
20
20
|
const pageActions = [
|
|
21
21
|
(0, invoicingCustomerListViewReducer_1.fetchInvoicingCustomerList)({ filters: {}, keepExistingListItems: false }),
|
|
22
|
+
// Plans only: the form's plan picker is the one consumer, and an
|
|
23
|
+
// unfiltered list would replace the store's plans with every catalog
|
|
24
|
+
// item.
|
|
22
25
|
(0, invoicingCatalogListViewReducer_1.fetchInvoicingCatalogPlanList)({
|
|
23
|
-
filters: {},
|
|
26
|
+
filters: { type: 'plan' },
|
|
24
27
|
keepExistingListItems: false,
|
|
25
28
|
}),
|
|
26
29
|
(0, couponViewReducer_1.fetchInvoicingCouponList)({ filters: {}, keepExistingListItems: false }),
|
package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js
CHANGED
|
@@ -20,7 +20,7 @@ const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => actions$.pi
|
|
|
20
20
|
const rootState = state$.value;
|
|
21
21
|
const viewState = rootState.editInvoicingSubscriptionDetailViewState;
|
|
22
22
|
const localData = (id != null
|
|
23
|
-
? viewState.editDraftById[id]
|
|
23
|
+
? viewState.editDraftById[id]?.localData
|
|
24
24
|
: viewState.newSubscriptionDraft) ?? viewState.newSubscriptionDraft;
|
|
25
25
|
// Shipping address captured via the shared Address screen is sent as an
|
|
26
26
|
// object in the payload (same pattern as the customer save flow).
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { RootState } from '../../../rootStateTypes';
|
|
4
|
-
import { seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail } from './editInvoicingSubscriptionDetailViewReducer';
|
|
5
|
-
export type ActionType = ReturnType<typeof updateInvoicingSubscriptionDetail> | ReturnType<typeof seedInvoicingSubscriptionFormDraft>;
|
|
4
|
+
import { fetchInvoicingSubscriptionFormPage, seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail } from './editInvoicingSubscriptionDetailViewReducer';
|
|
5
|
+
export type ActionType = ReturnType<typeof fetchInvoicingSubscriptionFormPage> | ReturnType<typeof updateInvoicingSubscriptionDetail> | ReturnType<typeof seedInvoicingSubscriptionFormDraft>;
|
|
6
6
|
/**
|
|
7
|
-
* Seeds the edit draft from the record the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
8
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
9
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
10
|
+
*
|
|
11
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
12
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
13
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
14
|
+
* or a cancel) would have nothing left to seed it.
|
|
11
15
|
*/
|
|
12
16
|
export declare const seedInvoicingSubscriptionFormDraftEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;
|
|
@@ -7,15 +7,24 @@ const editInvoicingSubscriptionDetailViewReducer_1 = require("./editInvoicingSub
|
|
|
7
7
|
const invoicingSubscriptionDetailHelper_1 = require("./invoicingSubscriptionDetailHelper");
|
|
8
8
|
const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
|
|
9
9
|
/**
|
|
10
|
-
* Seeds the edit draft from the record the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
11
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
12
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
13
|
+
*
|
|
14
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
15
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
16
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
17
|
+
* or a cancel) would have nothing left to seed it.
|
|
14
18
|
*/
|
|
15
|
-
const seedInvoicingSubscriptionFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)(editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionDetail.match
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
+
const seedInvoicingSubscriptionFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)((action) => editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionDetail.match(action) ||
|
|
20
|
+
editInvoicingSubscriptionDetailViewReducer_1.fetchInvoicingSubscriptionFormPage.match(action)), (0, operators_1.withLatestFrom)(state$), (0, operators_1.mergeMap)(([action, state]) => {
|
|
21
|
+
const id = editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionDetail.match(action)
|
|
22
|
+
? action.payload.id
|
|
23
|
+
: action.payload.subscriptionId;
|
|
24
|
+
// Nothing to seed from on a cold open; the detail response triggers this
|
|
25
|
+
// again once it lands.
|
|
26
|
+
const subscription = id != null ? (0, invoicingSubscriptionDetailHelper_1.getInvoicingSubscriptionById)(state, id) : undefined;
|
|
27
|
+
if (id == null || subscription == null) {
|
|
19
28
|
return rxjs_1.EMPTY;
|
|
20
29
|
}
|
|
21
30
|
return (0, rxjs_1.of)((0, editInvoicingSubscriptionDetailViewReducer_1.seedInvoicingSubscriptionFormDraft)({
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FetchState, ID } from '../../commonStateTypes/common';
|
|
2
|
+
/**
|
|
3
|
+
* A create/edit form's draft, plus where its values came from.
|
|
4
|
+
*
|
|
5
|
+
* The status is what lets an edit form tell "these are the record's values"
|
|
6
|
+
* from "these are whatever the form had at first render". It cannot be read off
|
|
7
|
+
* the presence of a draft, because the form writes drafts too: it pushes its
|
|
8
|
+
* whole state back on every change, so a change made before the record landed
|
|
9
|
+
* leaves a draft that looks seeded but holds nothing.
|
|
10
|
+
*
|
|
11
|
+
* `Not-Started` — written by the form, never filled from a record.
|
|
12
|
+
* `Completed` — filled from the loaded record; the form may have edited it since.
|
|
13
|
+
* `Error` — the record's fetch failed, so the form starts blank and a
|
|
14
|
+
* later successful fetch is still allowed to seed it.
|
|
15
|
+
*/
|
|
16
|
+
export interface InvoicingFormDraft<TLocalData> {
|
|
17
|
+
fetchState: FetchState;
|
|
18
|
+
localData: TLocalData;
|
|
19
|
+
}
|
|
20
|
+
export type InvoicingFormDraftById<TLocalData> = Record<ID, InvoicingFormDraft<TLocalData>>;
|
|
21
|
+
/**
|
|
22
|
+
* The form's own write. Keeps the status: an edit on top of a seeded draft is
|
|
23
|
+
* still the record's draft, and an edit made before the record arrived must not
|
|
24
|
+
* pass for one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function applyInvoicingFormDraftEdit<TLocalData>(draftById: InvoicingFormDraftById<TLocalData>, id: ID, localData: TLocalData): void;
|
|
27
|
+
/**
|
|
28
|
+
* Fill the draft from the loaded record, once. A second seed is ignored so a
|
|
29
|
+
* late refetch cannot overwrite edits in progress — but a draft the form wrote
|
|
30
|
+
* before the record landed is replaced, since it was never the record's.
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyInvoicingFormDraftSeed<TLocalData>(draftById: InvoicingFormDraftById<TLocalData>, id: ID, localData: TLocalData): void;
|
|
33
|
+
/**
|
|
34
|
+
* Stand the draft up blank when the record's fetch fails, so the form renders
|
|
35
|
+
* instead of waiting behind a skeleton that will never resolve. A draft that
|
|
36
|
+
* already exists is left alone: a background refetch failing is no reason to
|
|
37
|
+
* discard what the user has.
|
|
38
|
+
*/
|
|
39
|
+
export declare function applyInvoicingFormDraftFailure<TLocalData>(draftById: InvoicingFormDraftById<TLocalData>, id: ID, blankLocalData: TLocalData): void;
|
|
40
|
+
/** The draft's values, or the blank form when nothing is drafted yet. */
|
|
41
|
+
export declare function getInvoicingFormDraftLocalData<TLocalData>(draftById: InvoicingFormDraftById<TLocalData>, id: ID | undefined, blankLocalData: TLocalData): TLocalData;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the form should still be showing its skeleton — the record has
|
|
44
|
+
* neither reached the draft nor failed trying.
|
|
45
|
+
*
|
|
46
|
+
* Settles once and stays settled, which is what keeps a later refetch from
|
|
47
|
+
* swapping a form the user is typing into back for a skeleton. Create mode
|
|
48
|
+
* (no id) has no record to wait for.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isInvoicingFormDraftPending<TLocalData>(draftById: InvoicingFormDraftById<TLocalData>, id: ID | undefined): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyInvoicingFormDraftEdit = applyInvoicingFormDraftEdit;
|
|
4
|
+
exports.applyInvoicingFormDraftSeed = applyInvoicingFormDraftSeed;
|
|
5
|
+
exports.applyInvoicingFormDraftFailure = applyInvoicingFormDraftFailure;
|
|
6
|
+
exports.getInvoicingFormDraftLocalData = getInvoicingFormDraftLocalData;
|
|
7
|
+
exports.isInvoicingFormDraftPending = isInvoicingFormDraftPending;
|
|
8
|
+
/**
|
|
9
|
+
* The form's own write. Keeps the status: an edit on top of a seeded draft is
|
|
10
|
+
* still the record's draft, and an edit made before the record arrived must not
|
|
11
|
+
* pass for one.
|
|
12
|
+
*/
|
|
13
|
+
function applyInvoicingFormDraftEdit(draftById, id, localData) {
|
|
14
|
+
draftById[id] = {
|
|
15
|
+
fetchState: draftById[id]?.fetchState ?? 'Not-Started',
|
|
16
|
+
localData,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Fill the draft from the loaded record, once. A second seed is ignored so a
|
|
21
|
+
* late refetch cannot overwrite edits in progress — but a draft the form wrote
|
|
22
|
+
* before the record landed is replaced, since it was never the record's.
|
|
23
|
+
*/
|
|
24
|
+
function applyInvoicingFormDraftSeed(draftById, id, localData) {
|
|
25
|
+
if (draftById[id]?.fetchState === 'Completed') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
draftById[id] = { fetchState: 'Completed', localData };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Stand the draft up blank when the record's fetch fails, so the form renders
|
|
32
|
+
* instead of waiting behind a skeleton that will never resolve. A draft that
|
|
33
|
+
* already exists is left alone: a background refetch failing is no reason to
|
|
34
|
+
* discard what the user has.
|
|
35
|
+
*/
|
|
36
|
+
function applyInvoicingFormDraftFailure(draftById, id, blankLocalData) {
|
|
37
|
+
if (draftById[id] != null) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
draftById[id] = { fetchState: 'Error', localData: blankLocalData };
|
|
41
|
+
}
|
|
42
|
+
/** The draft's values, or the blank form when nothing is drafted yet. */
|
|
43
|
+
function getInvoicingFormDraftLocalData(draftById, id, blankLocalData) {
|
|
44
|
+
if (id == null) {
|
|
45
|
+
return blankLocalData;
|
|
46
|
+
}
|
|
47
|
+
return draftById[id]?.localData ?? blankLocalData;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether the form should still be showing its skeleton — the record has
|
|
51
|
+
* neither reached the draft nor failed trying.
|
|
52
|
+
*
|
|
53
|
+
* Settles once and stays settled, which is what keeps a later refetch from
|
|
54
|
+
* swapping a form the user is typing into back for a skeleton. Create mode
|
|
55
|
+
* (no id) has no record to wait for.
|
|
56
|
+
*/
|
|
57
|
+
function isInvoicingFormDraftPending(draftById, id) {
|
|
58
|
+
if (id == null) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const fetchState = draftById[id]?.fetchState;
|
|
62
|
+
return fetchState !== 'Completed' && fetchState !== 'Error';
|
|
63
|
+
}
|