@zeniai/client-epic-state 5.1.93 → 5.1.94

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.
Files changed (145) hide show
  1. package/lib/entity/aiCfo/aiCfoPayload.d.ts +7 -0
  2. package/lib/entity/aiCfo/aiCfoReducer.d.ts +3 -2
  3. package/lib/entity/aiCfo/aiCfoReducer.js +25 -2
  4. package/lib/entity/aiCfo/aiCfoState.d.ts +7 -0
  5. package/lib/entity/company/companyPayload.d.ts +4 -0
  6. package/lib/entity/company/companyPayload.js +18 -8
  7. package/lib/entity/company/companyStateTypes.d.ts +4 -0
  8. package/lib/entity/customerSatisfaction/customerSatisfactionReducer.d.ts +1 -1
  9. package/lib/entity/customerSatisfaction/customerSatisfactionReducer.js +6 -2
  10. package/lib/entity/snackbar/snackbarApiError.d.ts +8 -0
  11. package/lib/entity/snackbar/snackbarApiError.js +15 -0
  12. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  13. package/lib/entity/snackbar/snackbarTypes.js +31 -0
  14. package/lib/entity/tenant/tenantReducer.d.ts +5 -1
  15. package/lib/entity/tenant/tenantReducer.js +21 -2
  16. package/lib/epic.d.ts +3 -1
  17. package/lib/epic.js +5 -1
  18. package/lib/esm/entity/aiCfo/aiCfoReducer.js +23 -1
  19. package/lib/esm/entity/company/companyPayload.js +18 -8
  20. package/lib/esm/entity/customerSatisfaction/customerSatisfactionReducer.js +4 -1
  21. package/lib/esm/entity/snackbar/snackbarApiError.js +11 -0
  22. package/lib/esm/entity/snackbar/snackbarTypes.js +31 -0
  23. package/lib/esm/entity/tenant/tenantReducer.js +20 -1
  24. package/lib/esm/epic.js +5 -1
  25. package/lib/esm/index.js +9 -8
  26. package/lib/esm/view/companyHealthMetricView/companyHealthMetricConfigReducer.js +4 -1
  27. package/lib/esm/view/companyView/companyViewReducer.js +58 -1
  28. package/lib/esm/view/companyView/epic/updateCompanyProductServicesEpic.js +91 -0
  29. package/lib/esm/view/companyView/epic/updateCompanyQboRealmIdEpic.js +58 -0
  30. package/lib/esm/view/companyView/selector/companyManagementViewSelector.js +52 -0
  31. package/lib/esm/view/companyView/types/cockpitTypes.js +13 -1
  32. package/lib/esm/view/companyView/types/companyManagementViewState.js +2 -0
  33. package/lib/esm/view/invoicing/createCreditNote/createCreditNoteEpic.js +26 -4
  34. package/lib/esm/view/invoicing/createInvoice/createInvoiceEpic.js +26 -4
  35. package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +37 -10
  36. package/lib/esm/view/invoicing/discountAction/discountActionEpic.js +37 -10
  37. package/lib/esm/view/invoicing/dunningAction/dunningActionEpic.js +39 -10
  38. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -1
  39. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +5 -4
  40. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +35 -5
  41. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +25 -3
  42. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +26 -4
  43. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +26 -4
  44. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.js +20 -3
  45. package/lib/esm/view/invoicing/invoiceAction/invoiceActionEpic.js +65 -18
  46. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +30 -0
  47. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +4 -29
  48. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.js +26 -0
  49. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.js +16 -0
  50. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.js +9 -1
  51. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.js +50 -1
  52. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.js +14 -1
  53. package/lib/esm/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +31 -10
  54. package/lib/esm/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +30 -5
  55. package/lib/esm/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +26 -4
  56. package/lib/esm/view/invoicing/issueCreditNote/issueCreditNoteSelector.js +22 -14
  57. package/lib/esm/view/invoicing/recordPayment/recordPaymentEpic.js +49 -7
  58. package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +30 -9
  59. package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +1 -1
  60. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +56 -1
  61. package/lib/esm/view/invoicing/subscriptionAction/subscriptionActionEpic.js +45 -10
  62. package/lib/esm/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.js +4 -1
  63. package/lib/esm/view/vendorTabView/vendorTabViewReducer.js +4 -1
  64. package/lib/index.d.ts +17 -13
  65. package/lib/index.js +62 -50
  66. package/lib/view/companyHealthMetricView/companyHealthMetricConfigReducer.d.ts +1 -1
  67. package/lib/view/companyHealthMetricView/companyHealthMetricConfigReducer.js +5 -2
  68. package/lib/view/companyView/companyViewReducer.d.ts +22 -1
  69. package/lib/view/companyView/companyViewReducer.js +60 -3
  70. package/lib/view/companyView/epic/updateCompanyProductServicesEpic.d.ts +10 -0
  71. package/lib/view/companyView/epic/updateCompanyProductServicesEpic.js +95 -0
  72. package/lib/view/companyView/epic/updateCompanyQboRealmIdEpic.d.ts +8 -0
  73. package/lib/view/companyView/epic/updateCompanyQboRealmIdEpic.js +62 -0
  74. package/lib/view/companyView/selector/companyManagementViewSelector.d.ts +3 -0
  75. package/lib/view/companyView/selector/companyManagementViewSelector.js +55 -0
  76. package/lib/view/companyView/types/cockpitTypes.d.ts +7 -3
  77. package/lib/view/companyView/types/cockpitTypes.js +15 -1
  78. package/lib/view/companyView/types/companyManagementViewState.d.ts +2 -0
  79. package/lib/view/companyView/types/companyManagementViewState.js +2 -0
  80. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  81. package/lib/view/invoicing/createCreditNote/createCreditNoteEpic.d.ts +2 -1
  82. package/lib/view/invoicing/createCreditNote/createCreditNoteEpic.js +25 -3
  83. package/lib/view/invoicing/createInvoice/createInvoiceEpic.d.ts +2 -1
  84. package/lib/view/invoicing/createInvoice/createInvoiceEpic.js +25 -3
  85. package/lib/view/invoicing/createInvoice/createInvoiceSelector.d.ts +22 -8
  86. package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +37 -10
  87. package/lib/view/invoicing/discountAction/discountActionEpic.d.ts +2 -1
  88. package/lib/view/invoicing/discountAction/discountActionEpic.js +35 -8
  89. package/lib/view/invoicing/dunningAction/dunningActionEpic.d.ts +2 -1
  90. package/lib/view/invoicing/dunningAction/dunningActionEpic.js +37 -8
  91. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.d.ts +3 -1
  92. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -1
  93. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +5 -4
  94. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.d.ts +2 -1
  95. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +34 -4
  96. package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.d.ts +2 -1
  97. package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +24 -2
  98. package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.d.ts +2 -1
  99. package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +25 -3
  100. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.d.ts +2 -1
  101. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +25 -3
  102. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.d.ts +2 -1
  103. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.js +19 -2
  104. package/lib/view/invoicing/invoiceAction/invoiceActionEpic.d.ts +2 -1
  105. package/lib/view/invoicing/invoiceAction/invoiceActionEpic.js +63 -16
  106. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewPayload.d.ts +13 -0
  107. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.d.ts +1 -1
  108. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +30 -0
  109. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.d.ts +11 -31
  110. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +4 -29
  111. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewState.d.ts +15 -0
  112. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.d.ts +7 -0
  113. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.js +30 -0
  114. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.d.ts +6 -0
  115. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.js +20 -0
  116. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewEpics.d.ts +2 -2
  117. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.d.ts +12 -0
  118. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.js +11 -0
  119. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.d.ts +21 -4
  120. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.js +51 -2
  121. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.d.ts +3 -0
  122. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.js +16 -2
  123. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewState.d.ts +13 -0
  124. package/lib/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.d.ts +2 -1
  125. package/lib/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +30 -9
  126. package/lib/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +29 -4
  127. package/lib/view/invoicing/issueCreditNote/issueCreditNoteEpic.d.ts +2 -1
  128. package/lib/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +25 -3
  129. package/lib/view/invoicing/issueCreditNote/issueCreditNoteSelector.d.ts +24 -7
  130. package/lib/view/invoicing/issueCreditNote/issueCreditNoteSelector.js +23 -16
  131. package/lib/view/invoicing/recordPayment/recordPaymentEpic.d.ts +2 -1
  132. package/lib/view/invoicing/recordPayment/recordPaymentEpic.js +48 -6
  133. package/lib/view/invoicing/settingsView/settingsViewEpics.d.ts +2 -1
  134. package/lib/view/invoicing/settingsView/settingsViewEpics.js +28 -7
  135. package/lib/view/invoicing/settingsView/settingsViewReducer.d.ts +10 -1
  136. package/lib/view/invoicing/settingsView/settingsViewReducer.js +1 -1
  137. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +44 -2
  138. package/lib/view/invoicing/settingsView/settingsViewSelector.js +59 -2
  139. package/lib/view/invoicing/subscriptionAction/subscriptionActionEpic.d.ts +2 -1
  140. package/lib/view/invoicing/subscriptionAction/subscriptionActionEpic.js +43 -8
  141. package/lib/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.d.ts +1 -1
  142. package/lib/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.js +5 -2
  143. package/lib/view/vendorTabView/vendorTabViewReducer.d.ts +1 -1
  144. package/lib/view/vendorTabView/vendorTabViewReducer.js +5 -2
  145. package/package.json +2 -2
@@ -0,0 +1,26 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, map, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
4
+ import { toInvoicingDunningEmailPreview, } from './invoicingDunningCaseDetailViewPayload';
5
+ import { fetchInvoicingDunningEmailPreview, updateInvoicingDunningEmailPreview, updateInvoicingDunningEmailPreviewFailure, } from './invoicingDunningCaseDetailViewReducer';
6
+ export const fetchInvoicingDunningEmailPreviewEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingDunningEmailPreview.match), switchMap((action) => {
7
+ const { id } = action.payload;
8
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/dunning/${encodeURIComponent(id)}/email-preview`;
9
+ return zeniAPI
10
+ .postAndGetJSON(apiUrl, {})
11
+ .pipe(map((response) => {
12
+ if (isSuccessResponse(response) && response.data != null) {
13
+ return updateInvoicingDunningEmailPreview({
14
+ id,
15
+ preview: toInvoicingDunningEmailPreview(response.data),
16
+ });
17
+ }
18
+ return updateInvoicingDunningEmailPreviewFailure({
19
+ id,
20
+ status: response.status,
21
+ });
22
+ }), catchError((error) => of(updateInvoicingDunningEmailPreviewFailure({
23
+ id,
24
+ status: createZeniAPIStatus('Unexpected Error', `Dunning email preview REST API call errored out: ${error instanceof Error ? error.message : String(error)}`),
25
+ }))));
26
+ }));
@@ -0,0 +1,16 @@
1
+ import { EMPTY, of } from 'rxjs';
2
+ import { filter, mergeMap } from 'rxjs/operators';
3
+ import { fetchInvoicingDunningEmailPreview, fetchInvoicingDunningEmailPreviewPage, } from './invoicingDunningCaseDetailViewReducer';
4
+ const shouldFetch = (fetchStateAndError, cacheOverride) => cacheOverride ||
5
+ (fetchStateAndError.fetchState !== 'Completed' &&
6
+ fetchStateAndError.fetchState !== 'In-Progress');
7
+ export const fetchInvoicingDunningEmailPreviewPageEpic = (actions$, state$) => actions$.pipe(filter(fetchInvoicingDunningEmailPreviewPage.match), mergeMap((action) => {
8
+ const { cacheOverride, id } = action.payload;
9
+ const detailState = state$.value.invoicingDunningCaseDetailViewState;
10
+ if (detailState.id !== id) {
11
+ return EMPTY;
12
+ }
13
+ return shouldFetch(detailState.emailPreview, cacheOverride)
14
+ ? of(fetchInvoicingDunningEmailPreview({ id }))
15
+ : EMPTY;
16
+ }));
@@ -1 +1,9 @@
1
- export {};
1
+ export const toInvoicingDunningEmailPreview = (payload) => ({
2
+ body: payload.body,
3
+ bodyToken: payload.body_token,
4
+ cc: payload.cc,
5
+ renderedHtmlTemplate: payload.rendered_html_template,
6
+ subject: payload.subject,
7
+ subjectToken: payload.subject_token,
8
+ to: payload.to,
9
+ });
@@ -1,8 +1,14 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { clearAll } from '../../../rootActions';
3
+ export const initialEmailPreviewState = {
4
+ fetchState: 'Not-Started',
5
+ error: undefined,
6
+ preview: undefined,
7
+ };
3
8
  export const initialState = {
4
9
  fetchState: 'Not-Started',
5
10
  error: undefined,
11
+ emailPreview: initialEmailPreviewState,
6
12
  id: undefined,
7
13
  };
8
14
  const invoicingDunningCaseDetailView = createSlice({
@@ -10,10 +16,38 @@ const invoicingDunningCaseDetailView = createSlice({
10
16
  initialState,
11
17
  reducers: {
12
18
  fetchInvoicingDunningCaseDetail(draft, action) {
19
+ if (draft.id !== action.payload.id) {
20
+ draft.emailPreview = { ...initialEmailPreviewState };
21
+ }
13
22
  draft.id = action.payload.id;
14
23
  draft.fetchState = 'In-Progress';
15
24
  draft.error = undefined;
16
25
  },
26
+ fetchInvoicingDunningEmailPreview(draft, action) {
27
+ if (draft.id !== action.payload.id) {
28
+ return;
29
+ }
30
+ draft.emailPreview.fetchState = 'In-Progress';
31
+ draft.emailPreview.error = undefined;
32
+ },
33
+ fetchInvoicingDunningEmailPreviewPage: {
34
+ reducer() {
35
+ // Cache-aware orchestrator trigger only.
36
+ },
37
+ prepare(payload) {
38
+ return {
39
+ payload: {
40
+ cacheOverride: payload.cacheOverride ?? false,
41
+ id: payload.id,
42
+ },
43
+ };
44
+ },
45
+ },
46
+ invalidateInvoicingDunningEmailPreview(draft, action) {
47
+ if (draft.id === action.payload.id) {
48
+ draft.emailPreview = { ...initialEmailPreviewState };
49
+ }
50
+ },
17
51
  updateInvoicingDunningCaseDetail(draft, action) {
18
52
  draft.id = action.payload.id;
19
53
  draft.fetchState = 'Completed';
@@ -23,6 +57,21 @@ const invoicingDunningCaseDetailView = createSlice({
23
57
  draft.fetchState = 'Error';
24
58
  draft.error = action.payload;
25
59
  },
60
+ updateInvoicingDunningEmailPreview(draft, action) {
61
+ if (draft.id === action.payload.id) {
62
+ draft.emailPreview = {
63
+ error: undefined,
64
+ fetchState: 'Completed',
65
+ preview: action.payload.preview,
66
+ };
67
+ }
68
+ },
69
+ updateInvoicingDunningEmailPreviewFailure(draft, action) {
70
+ if (draft.id === action.payload.id) {
71
+ draft.emailPreview.fetchState = 'Error';
72
+ draft.emailPreview.error = action.payload.status;
73
+ }
74
+ },
26
75
  clearInvoicingDunningCaseDetailView(draft) {
27
76
  Object.assign(draft, initialState);
28
77
  },
@@ -33,5 +82,5 @@ const invoicingDunningCaseDetailView = createSlice({
33
82
  });
34
83
  },
35
84
  });
36
- export const { fetchInvoicingDunningCaseDetail, updateInvoicingDunningCaseDetail, updateInvoicingDunningCaseDetailFailure, clearInvoicingDunningCaseDetailView, } = invoicingDunningCaseDetailView.actions;
85
+ export const { clearInvoicingDunningCaseDetailView, fetchInvoicingDunningCaseDetail, fetchInvoicingDunningEmailPreview, fetchInvoicingDunningEmailPreviewPage, invalidateInvoicingDunningEmailPreview, updateInvoicingDunningCaseDetail, updateInvoicingDunningCaseDetailFailure, updateInvoicingDunningEmailPreview, updateInvoicingDunningEmailPreviewFailure, } = invoicingDunningCaseDetailView.actions;
37
86
  export default invoicingDunningCaseDetailView.reducer;
@@ -1,13 +1,26 @@
1
1
  import { getInvoicingDunningCaseById as getInvoicingDunningCaseByIdFromState } from '../../../entity/invoicing/dunningCase/dunningCaseSelector';
2
2
  import { getInvoicingDunningActionState } from '../dunningAction/dunningActionSelector';
3
+ const EMAIL_PREVIEW_NOT_STARTED = {
4
+ error: undefined,
5
+ fetchState: 'Not-Started',
6
+ preview: undefined,
7
+ };
3
8
  export const getInvoicingDunningCaseById = (state, id) => getInvoicingDunningCaseByIdFromState(state.invoicingDunningCaseState, id);
4
9
  export const getInvoicingDunningCaseDetail = (state, invoicingDunningCaseID) => {
5
- const { error, fetchState } = state.invoicingDunningCaseDetailViewState;
10
+ const { emailPreview, error, fetchState, id } = state.invoicingDunningCaseDetailViewState;
6
11
  return {
7
12
  actionState: getInvoicingDunningActionState(invoicingDunningCaseID)(state),
8
13
  dunningCase: getInvoicingDunningCaseById(state, invoicingDunningCaseID),
14
+ emailPreview: id === invoicingDunningCaseID ? emailPreview : EMAIL_PREVIEW_NOT_STARTED,
9
15
  fetchState,
10
16
  error,
11
17
  version: 1.0,
12
18
  };
13
19
  };
20
+ export const getInvoicingDunningEmailPreview = (state, invoicingDunningCaseID) => {
21
+ const detailState = state.invoicingDunningCaseDetailViewState;
22
+ if (detailState.id !== invoicingDunningCaseID) {
23
+ return EMAIL_PREVIEW_NOT_STARTED;
24
+ }
25
+ return detailState.emailPreview;
26
+ };
@@ -1,5 +1,7 @@
1
- import { of } from 'rxjs';
1
+ import { from, of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
4
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
3
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
6
  import { fetchInvoicingQboSyncHealth, retryInvoicingQboSyncTask, retryInvoicingQboSyncTaskFailure, retryInvoicingQboSyncTaskSuccess, } from '../invoicingQboViewReducer';
5
7
  export const retryInvoicingQboSyncTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(retryInvoicingQboSyncTask.match), mergeMap((action) => {
@@ -12,15 +14,34 @@ export const retryInvoicingQboSyncTaskEpic = (actions$, _state$, zeniAPI) => act
12
14
  return of(retryInvoicingQboSyncTaskSuccess({
13
15
  status: response.data?.status ?? '',
14
16
  taskId: response.data?.task_id ?? taskId,
15
- }), fetchInvoicingQboSyncHealth());
17
+ }), fetchInvoicingQboSyncHealth(), openSnackbar({
18
+ messageSection: 'invoicing_qbo_sync_health_retry',
19
+ messageText: 'success',
20
+ type: 'success',
21
+ }));
16
22
  }
17
- return of(retryInvoicingQboSyncTaskFailure({
18
- status: response.status,
23
+ return from([
24
+ retryInvoicingQboSyncTaskFailure({
25
+ status: response.status,
26
+ taskId,
27
+ }),
28
+ openSnackbar({
29
+ messageSection: 'invoicing_qbo_sync_health_retry',
30
+ messageText: 'failed',
31
+ type: 'error',
32
+ variables: apiErrorSnackbarVariables(response.status),
33
+ }),
34
+ ]);
35
+ }), catchError((error) => from([
36
+ retryInvoicingQboSyncTaskFailure({
37
+ status: createZeniAPIStatus('Unexpected Error', 'Retry QBO sync task REST API call errored out ' +
38
+ JSON.stringify(error)),
19
39
  taskId,
20
- }));
21
- }), catchError((error) => of(retryInvoicingQboSyncTaskFailure({
22
- status: createZeniAPIStatus('Unexpected Error', 'Retry QBO sync task REST API call errored out ' +
23
- JSON.stringify(error)),
24
- taskId,
25
- }))));
40
+ }),
41
+ openSnackbar({
42
+ messageSection: 'invoicing_qbo_sync_health_retry',
43
+ messageText: 'failed',
44
+ type: 'error',
45
+ }),
46
+ ])));
26
47
  }));
@@ -1,5 +1,7 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
4
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
3
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
6
  import { fetchInvoicingQboSyncHealth, startInvoicingQboBackfill, startInvoicingQboBackfillFailure, startInvoicingQboBackfillSuccess, } from '../invoicingQboViewReducer';
5
7
  export const startInvoicingQboBackfillEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(startInvoicingQboBackfill.match), switchMap((action) => {
@@ -10,9 +12,32 @@ export const startInvoicingQboBackfillEpic = (actions$, _state$, zeniAPI) => act
10
12
  })
11
13
  .pipe(mergeMap((response) => {
12
14
  if (isSuccessResponse(response)) {
13
- return of(startInvoicingQboBackfillSuccess(), fetchInvoicingQboSyncHealth());
15
+ return from([
16
+ startInvoicingQboBackfillSuccess(),
17
+ fetchInvoicingQboSyncHealth(),
18
+ openSnackbar({
19
+ messageSection: 'invoicing_qbo_backfill',
20
+ messageText: 'success',
21
+ type: 'success',
22
+ }),
23
+ ]);
14
24
  }
15
- return of(startInvoicingQboBackfillFailure(response.status));
16
- }), catchError((error) => of(startInvoicingQboBackfillFailure(createZeniAPIStatus('Unexpected Error', 'Start QBO backfill REST API call errored out ' +
17
- JSON.stringify(error))))));
25
+ return from([
26
+ startInvoicingQboBackfillFailure(response.status),
27
+ openSnackbar({
28
+ messageSection: 'invoicing_qbo_backfill',
29
+ messageText: 'failed',
30
+ type: 'error',
31
+ variables: apiErrorSnackbarVariables(response.status),
32
+ }),
33
+ ]);
34
+ }), catchError((error) => from([
35
+ startInvoicingQboBackfillFailure(createZeniAPIStatus('Unexpected Error', 'Start QBO backfill REST API call errored out ' +
36
+ JSON.stringify(error))),
37
+ openSnackbar({
38
+ messageSection: 'invoicing_qbo_backfill',
39
+ messageText: 'failed',
40
+ type: 'error',
41
+ }),
42
+ ])));
18
43
  }));
@@ -1,6 +1,8 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateInvoicingCreditNotes } from '../../../entity/invoicing/creditNote/creditNoteReducer';
4
+ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
5
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
6
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
5
7
  import { fetchInvoiceDetail } from '../invoiceDetail/invoiceDetailReducer';
6
8
  import { localDataToIssueCreditNotePayload, } from './issueCreditNotePayload';
@@ -18,9 +20,29 @@ export const issueCreditNoteEpic = (actions$, state$, zeniAPI) => actions$.pipe(
18
20
  updateInvoicingCreditNotes([response.data], 'merge'),
19
21
  fetchInvoiceDetail({ invoiceId }),
20
22
  issueCreditNoteSuccess({ creditNoteId: response.data.id }),
23
+ openSnackbar({
24
+ messageSection: 'invoicing_credit_note_saved',
25
+ messageText: 'success',
26
+ type: 'success',
27
+ }),
21
28
  ];
22
29
  }
23
- return of(issueCreditNoteFailure(response.status));
24
- }), catchError((error) => of(issueCreditNoteFailure(createZeniAPIStatus('Unexpected Error', 'Issue credit note REST API call errored out ' +
25
- JSON.stringify(error))))));
30
+ return from([
31
+ issueCreditNoteFailure(response.status),
32
+ openSnackbar({
33
+ messageSection: 'invoicing_credit_note_saved',
34
+ messageText: 'failed',
35
+ type: 'error',
36
+ variables: apiErrorSnackbarVariables(response.status),
37
+ }),
38
+ ]);
39
+ }), catchError((error) => from([
40
+ issueCreditNoteFailure(createZeniAPIStatus('Unexpected Error', 'Issue credit note REST API call errored out ' +
41
+ JSON.stringify(error))),
42
+ openSnackbar({
43
+ messageSection: 'invoicing_credit_note_saved',
44
+ messageText: 'failed',
45
+ type: 'error',
46
+ }),
47
+ ])));
26
48
  }));
@@ -1,20 +1,28 @@
1
- import { INVOICING_CREDIT_NOTE_REASON_CODES } from './issueCreditNoteFormConfig';
2
- export const getIssueCreditNoteView = (state) => {
3
- const { error, fetchState, issuedCreditNoteId } = state.invoicingIssueCreditNoteState;
1
+ import { getInvoiceDetail } from '../invoiceDetail/invoiceDetailSelector';
2
+ import { INVOICING_CREDIT_NOTE_REASON_CODES, initialIssueCreditNoteFormLocalData, } from './issueCreditNoteFormConfig';
3
+ /**
4
+ * The draft is a single global slice, so a draft seeded for a previously opened
5
+ * invoice can still be in state when another invoice's screen mounts. Anything
6
+ * that would render that stale draft next to this invoice is dropped until the
7
+ * screen re-seeds it for `invoiceId`.
8
+ */
9
+ export const getIssueCreditNoteView = (state, invoiceId) => {
10
+ const invoiceView = getInvoiceDetail(state, invoiceId);
11
+ const { draft, error, fetchState, issuedCreditNoteId } = state.invoicingIssueCreditNoteState;
12
+ const isDraftForInvoice = draft.invoiceId === invoiceId;
4
13
  return {
5
- error,
6
- fetchState,
14
+ invoice: invoiceView.invoice,
15
+ isPageReady: invoiceView.fetchState === 'Completed' &&
16
+ isDraftForInvoice &&
17
+ draft.lineItems.length > 0,
7
18
  issuedCreditNoteId,
8
- version: 1.0,
9
- };
10
- };
11
- export const getIssueCreditNoteFormView = (state) => {
12
- const { draft, error, fetchState } = state.invoicingIssueCreditNoteState;
13
- return {
14
- localData: draft,
19
+ localData: isDraftForInvoice
20
+ ? draft
21
+ : initialIssueCreditNoteFormLocalData(invoiceId),
15
22
  reasonCodeValues: [...INVOICING_CREDIT_NOTE_REASON_CODES],
16
- error,
17
- fetchState,
23
+ submitState: { error, fetchState },
24
+ error: invoiceView.error,
25
+ fetchState: invoiceView.fetchState,
18
26
  version: 1.0,
19
27
  };
20
28
  };
@@ -1,7 +1,9 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateInvoices } from '../../../entity/invoicing/invoice/invoiceReducer';
4
4
  import { updateInvoicingTransactions } from '../../../entity/invoicing/invoicingTransaction/invoicingTransactionReducer';
5
+ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
6
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
5
7
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
6
8
  import { fetchInvoiceDetail } from '../invoiceDetail/invoiceDetailReducer';
7
9
  import { addInvoicingTransactionToList } from '../invoicingTransactionListView/invoicingTransactionListViewReducer';
@@ -26,11 +28,31 @@ export const recordPaymentEpic = (actions$, state$, zeniAPI) => actions$.pipe(fi
26
28
  updateInvoices([response.data], 'merge'),
27
29
  fetchInvoiceDetail({ invoiceId }),
28
30
  recordPaymentSuccess({ invoiceId }),
31
+ openSnackbar({
32
+ messageSection: 'invoicing_payment_recorded',
33
+ messageText: 'success',
34
+ type: 'success',
35
+ }),
29
36
  ];
30
37
  }
31
- return of(recordPaymentFailure(response.status));
32
- }), catchError((error) => of(recordPaymentFailure(createZeniAPIStatus('Unexpected Error', 'Record payment REST API call errored out ' +
33
- JSON.stringify(error))))));
38
+ return from([
39
+ recordPaymentFailure(response.status),
40
+ openSnackbar({
41
+ messageSection: 'invoicing_payment_recorded',
42
+ messageText: 'failed',
43
+ type: 'error',
44
+ variables: apiErrorSnackbarVariables(response.status),
45
+ }),
46
+ ]);
47
+ }), catchError((error) => from([
48
+ recordPaymentFailure(createZeniAPIStatus('Unexpected Error', 'Record payment REST API call errored out ' +
49
+ JSON.stringify(error))),
50
+ openSnackbar({
51
+ messageSection: 'invoicing_payment_recorded',
52
+ messageText: 'failed',
53
+ type: 'error',
54
+ }),
55
+ ])));
34
56
  }
35
57
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/payments`;
36
58
  const data = localDataToRecordPaymentPayload(draft);
@@ -49,9 +71,29 @@ export const recordPaymentEpic = (actions$, state$, zeniAPI) => actions$.pipe(fi
49
71
  if (linkedInvoiceId != null) {
50
72
  successActions.push(fetchInvoiceDetail({ invoiceId: linkedInvoiceId }));
51
73
  }
74
+ successActions.push(openSnackbar({
75
+ messageSection: 'invoicing_payment_recorded',
76
+ messageText: 'success',
77
+ type: 'success',
78
+ }));
52
79
  return successActions;
53
80
  }
54
- return of(recordPaymentFailure(response.status));
55
- }), catchError((error) => of(recordPaymentFailure(createZeniAPIStatus('Unexpected Error', 'Record payment REST API call errored out ' +
56
- JSON.stringify(error))))));
81
+ return from([
82
+ recordPaymentFailure(response.status),
83
+ openSnackbar({
84
+ messageSection: 'invoicing_payment_recorded',
85
+ messageText: 'failed',
86
+ type: 'error',
87
+ variables: apiErrorSnackbarVariables(response.status),
88
+ }),
89
+ ]);
90
+ }), catchError((error) => from([
91
+ recordPaymentFailure(createZeniAPIStatus('Unexpected Error', 'Record payment REST API call errored out ' +
92
+ JSON.stringify(error))),
93
+ openSnackbar({
94
+ messageSection: 'invoicing_payment_recorded',
95
+ messageText: 'failed',
96
+ type: 'error',
97
+ }),
98
+ ])));
57
99
  }));
@@ -1,7 +1,9 @@
1
- import { of } from 'rxjs';
1
+ import { from, of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { updateAddresses } from '../../../entity/address/addressReducer';
4
- import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
4
+ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
5
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
6
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
5
7
  import { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, updateInvoicingSettings, updateInvoicingSettingsFailure, updateInvoicingStripeConnection, } from './settingsViewReducer';
6
8
  export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSettings.match), switchMap(() => {
7
9
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
@@ -57,15 +59,34 @@ export const disconnectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => act
57
59
  }));
58
60
  export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
59
61
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
62
+ const { settings, snackbarSection } = action.payload;
63
+ /** `status` surfaces the platform's error text; omit it to keep the generic copy. */
64
+ const snackbar = (messageText, status) => snackbarSection == null
65
+ ? []
66
+ : [
67
+ openSnackbar({
68
+ messageSection: snackbarSection,
69
+ messageText,
70
+ type: messageText === 'success' ? 'success' : 'error',
71
+ variables: apiErrorSnackbarVariables(status),
72
+ }),
73
+ ];
60
74
  return zeniAPI
61
- .putAndGetJSON(apiUrl, {
62
- ...action.payload,
63
- })
75
+ .putAndGetJSON(apiUrl, { ...settings })
64
76
  .pipe(switchMap((response) => {
65
77
  if (isSuccessResponse(response) && response.data != null) {
66
- return of(saveInvoicingSettingsSuccess(response.data));
78
+ return from([
79
+ saveInvoicingSettingsSuccess(response.data),
80
+ ...snackbar('success'),
81
+ ]);
67
82
  }
68
- return of(saveInvoicingSettingsFailure(response.status));
69
- }), catchError((error) => of(saveInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Save settings REST API call errored out ' +
70
- JSON.stringify(error))))));
83
+ return from([
84
+ saveInvoicingSettingsFailure(response.status),
85
+ ...snackbar('failed', response.status),
86
+ ]);
87
+ }), catchError((error) => from([
88
+ saveInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Save settings REST API call errored out ' +
89
+ JSON.stringify(error))),
90
+ ...snackbar('failed'),
91
+ ])));
71
92
  }));
@@ -30,7 +30,7 @@ const settingsView = createSlice({
30
30
  saveInvoicingSettings(draft, action) {
31
31
  draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
32
32
  if (draft.settings != null) {
33
- draft.settings = { ...draft.settings, ...action.payload };
33
+ draft.settings = { ...draft.settings, ...action.payload.settings };
34
34
  }
35
35
  },
36
36
  saveInvoicingSettingsSuccess(draft, action) {
@@ -1,4 +1,6 @@
1
+ import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
1
2
  import { getFormattedAddress } from '../../addressView/addressViewSelector';
3
+ import { getInvoicingConfigView } from '../invoicingConfigView/invoicingConfigViewSelector';
2
4
  import { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
3
5
  /**
4
6
  * Shared primitive: the raw invoicing settings. Consumed internally by other
@@ -29,11 +31,63 @@ export const getInvoicingSettingsView = (state) => {
29
31
  stripeConnectState: stripeFetchState,
30
32
  };
31
33
  };
34
+ /**
35
+ * Gates the Enable Invoicing action: payments must be able to land (Stripe
36
+ * connected *and* accepting charges) and the QBO accounting step must be
37
+ * finished, so an enabled tenant can always book what it invoices.
38
+ *
39
+ * Deliberately not part of `deriveInvoicingOnboardingStatus`, which routes the
40
+ * module off the config flags alone. Folding these in there would (a) demote a
41
+ * live tenant to the settings tree the moment an integration broke, stranding
42
+ * its existing invoices, and (b) deadlock the route gate, which fetches only the
43
+ * config — never the settings or QBO connection these read.
44
+ */
45
+ export function deriveInvoicingEnablePrerequisites(settings, isQboConnected, qboAccountMappingKindOptions) {
46
+ const isStripeConnected = settings?.stripe_connected === true &&
47
+ settings?.stripe_account?.charges_enabled === true;
48
+ const accountMappings = settings?.qbo?.account_mappings ?? undefined;
49
+ // An empty option list means the config has not landed yet — treating that as
50
+ // configured would let `every` pass vacuously and unlock the button early.
51
+ const isQboConfigured = isQboConnected &&
52
+ qboAccountMappingKindOptions.length > 0 &&
53
+ qboAccountMappingKindOptions.every((option) => getInvoicingQboAccountMappingId(accountMappings, option.code) != null);
54
+ return {
55
+ canEnable: isStripeConnected && isQboConfigured,
56
+ isQboConfigured,
57
+ isStripeConnected,
58
+ };
59
+ }
60
+ /**
61
+ * The settings hub's composite of everything it derives from the store: whether
62
+ * to offer Enable Invoicing at all, the in-flight flag for that action, and the
63
+ * two integration prerequisites kept separate so the hub can name the step still
64
+ * outstanding instead of showing a dead button.
65
+ *
66
+ * `canEnableInvoicing` folds in the onboarding step, so the screen does not need
67
+ * to re-check it: the footer is only offered mid-onboarding and only once both
68
+ * integrations are ready.
69
+ *
70
+ * Deliberately not folded into `getInvoicingConfigView`: that selector gates
71
+ * every invoicing route and stays a pure read of the config slice. The settings
72
+ * and QBO slices read here are only fetched once a settings screen is mounted.
73
+ */
74
+ export const getInvoicingSettingsHubView = (state) => {
75
+ const { enableState, qboAccountMappingKindOptions, status } = getInvoicingConfigView(state);
76
+ const { canEnable, isQboConfigured, isStripeConnected } = deriveInvoicingEnablePrerequisites(state.invoicingSettingsViewState.settings, state.invoicingQboViewState.connection?.connected === true, qboAccountMappingKindOptions);
77
+ return {
78
+ canEnableInvoicing: status === 'settings' && canEnable,
79
+ isEnablingInvoicing: enableState.fetchState === 'In-Progress',
80
+ isQboConfigured,
81
+ isStripeConnected,
82
+ status,
83
+ };
84
+ };
32
85
  /**
33
86
  * Bundled view for the branding settings form: the draft `localData` (seeded
34
87
  * from the existing settings until the user edits), the business-address label
35
88
  * (from the shared Address screen, seeded on load from `address_id`), the raw
36
- * settings (for the logo/preview) and the save fetch state.
89
+ * settings (for the logo/preview), the backend date-format choices and the
90
+ * save fetch state.
37
91
  */
38
92
  export const getInvoicingBrandingFormView = (state) => {
39
93
  const { brandingDraft, saveFetchState, settings } = state.invoicingSettingsViewState;
@@ -43,6 +97,7 @@ export const getInvoicingBrandingFormView = (state) => {
43
97
  : undefined;
44
98
  return {
45
99
  addressLabel,
100
+ dateFormatOptions: getInvoicingConfigView(state).dateFormatOptions,
46
101
  localData: brandingDraft ?? entityToInvoicingBrandingFormLocalData(settings),
47
102
  saveState: saveFetchState,
48
103
  settings,
@@ -1,13 +1,41 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateAddresses } from '../../../entity/address/addressReducer';
4
4
  import { extractInvoicingSubscriptionAddresses } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload';
5
5
  import { updateInvoicingSubscriptions } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionReducer';
6
- import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
6
+ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
7
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
8
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
7
9
  import { runInvoicingSubscriptionAction, runInvoicingSubscriptionActionFailure, runInvoicingSubscriptionActionSuccess, } from './subscriptionActionReducer';
10
+ /**
11
+ * Each lifecycle action reports with its own copy. Keyed loosely because the
12
+ * action payload carries a plain `string`; an unmapped kind stays silent.
13
+ */
14
+ const SNACKBAR_SECTION_BY_ACTION = {
15
+ activate: 'invoicing_subscription_activated',
16
+ cancel: 'invoicing_subscription_cancelled',
17
+ change_term_end: 'invoicing_subscription_term_end_changed',
18
+ pause: 'invoicing_subscription_paused',
19
+ reactivate: 'invoicing_subscription_reactivated',
20
+ remove_scheduled_cancellation: 'invoicing_subscription_cancellation_removed',
21
+ resume: 'invoicing_subscription_resumed',
22
+ set_non_renewing: 'invoicing_subscription_non_renewing',
23
+ };
8
24
  export const runInvoicingSubscriptionActionEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(runInvoicingSubscriptionAction.match), mergeMap((action) => {
9
25
  const { action: kind, body, id } = action.payload;
10
26
  const requestBody = { action: kind, ...(body ?? {}) };
27
+ const snackbarSection = SNACKBAR_SECTION_BY_ACTION[kind];
28
+ /** `status` surfaces the platform's error text; omit it to keep the generic copy. */
29
+ const snackbar = (messageText, status) => snackbarSection == null
30
+ ? []
31
+ : [
32
+ openSnackbar({
33
+ messageSection: snackbarSection,
34
+ messageText,
35
+ type: messageText === 'success' ? 'success' : 'error',
36
+ variables: apiErrorSnackbarVariables(status),
37
+ }),
38
+ ];
11
39
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions/${encodeURIComponent(id)}/actions`;
12
40
  return zeniAPI
13
41
  .postAndGetJSON(apiUrl, requestBody)
@@ -17,15 +45,22 @@ export const runInvoicingSubscriptionActionEpic = (actions$, _state$, zeniAPI) =
17
45
  updateInvoicingSubscriptions([response.data], 'merge'),
18
46
  updateAddresses(extractInvoicingSubscriptionAddresses([response.data])),
19
47
  runInvoicingSubscriptionActionSuccess({ id }),
48
+ ...snackbar('success'),
20
49
  ];
21
50
  }
22
- return of(runInvoicingSubscriptionActionFailure({
23
- error: response.status,
51
+ return from([
52
+ runInvoicingSubscriptionActionFailure({
53
+ error: response.status,
54
+ id,
55
+ }),
56
+ ...snackbar('failed', response.status),
57
+ ]);
58
+ }), catchError((error) => from([
59
+ runInvoicingSubscriptionActionFailure({
60
+ error: createZeniAPIStatus('Unexpected Error', 'Subscription action REST API call errored out ' +
61
+ JSON.stringify(error)),
24
62
  id,
25
- }));
26
- }), catchError((error) => of(runInvoicingSubscriptionActionFailure({
27
- error: createZeniAPIStatus('Unexpected Error', 'Subscription action REST API call errored out ' +
28
- JSON.stringify(error)),
29
- id,
30
- }))));
63
+ }),
64
+ ...snackbar('failed'),
65
+ ])));
31
66
  }));