@zeniai/client-epic-state 5.2.40 → 5.2.41

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 (20) hide show
  1. package/lib/entity/file/fileReducer.d.ts +8 -0
  2. package/lib/entity/file/fileReducer.js +10 -2
  3. package/lib/entity/invoicing/invoicingCommonPayload.d.ts +14 -1
  4. package/lib/esm/entity/file/fileReducer.js +10 -2
  5. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +1 -1
  6. package/lib/esm/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +6 -0
  7. package/lib/esm/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +7 -0
  8. package/lib/esm/view/invoicing/settingsView/invoicingBrandingFormConfig.js +1 -1
  9. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +5 -0
  10. package/lib/esm/view/invoicing/settingsView/submitInvoicingBrandingFormEpic.js +9 -0
  11. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +1 -1
  12. package/lib/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +6 -0
  13. package/lib/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +7 -0
  14. package/lib/view/invoicing/settingsView/epics/settingsViewActionType.d.ts +2 -1
  15. package/lib/view/invoicing/settingsView/invoicingBrandingFormConfig.js +1 -1
  16. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +8 -0
  17. package/lib/view/invoicing/settingsView/settingsViewSelector.js +5 -0
  18. package/lib/view/invoicing/settingsView/submitInvoicingBrandingFormEpic.d.ts +2 -1
  19. package/lib/view/invoicing/settingsView/submitInvoicingBrandingFormEpic.js +9 -0
  20. package/package.json +1 -1
@@ -11,6 +11,14 @@ export default _default;
11
11
  /**
12
12
  * Helper functions.
13
13
  */
14
+ /**
15
+ * `image_file_ids` and `image_files` are typed as required, but a service that
16
+ * has no renditions to report can leave them out — the invoicing settings
17
+ * `logo_file` did. Mapping an absent `image_files` threw, taking down whichever
18
+ * reducer was mapping the file rather than losing one field, so both default to
19
+ * empty here: an absent list means no renditions, which is what an empty list
20
+ * already says.
21
+ */
14
22
  export declare function mapFilePayloadToFile(filepayload: FilePayload): File;
15
23
  export declare function mapAttachmentFilePayloadToFile(filePayload: AttachmentFilePayload): AttachmentFile;
16
24
  export declare function mapAttachmentFileToAttachmentFilePayload(file: AttachmentFile | undefined): AttachmentFilePayload | undefined;
@@ -48,6 +48,14 @@ exports.default = user.reducer;
48
48
  /**
49
49
  * Helper functions.
50
50
  */
51
+ /**
52
+ * `image_file_ids` and `image_files` are typed as required, but a service that
53
+ * has no renditions to report can leave them out — the invoicing settings
54
+ * `logo_file` did. Mapping an absent `image_files` threw, taking down whichever
55
+ * reducer was mapping the file rather than losing one field, so both default to
56
+ * empty here: an absent list means no renditions, which is what an empty list
57
+ * already says.
58
+ */
51
59
  function mapFilePayloadToFile(filepayload) {
52
60
  return {
53
61
  fileSizeUnit: filepayload.file_size_unit,
@@ -55,9 +63,9 @@ function mapFilePayloadToFile(filepayload) {
55
63
  ? (0, zeniDayJS_1.date)(filepayload.update_time)
56
64
  : undefined,
57
65
  userId: filepayload.user_id ?? undefined,
58
- imageFileIds: filepayload.image_file_ids,
66
+ imageFileIds: filepayload.image_file_ids ?? [],
59
67
  documentSide: filepayload.document_side,
60
- imageFiles: filepayload.image_files.map((imagePayload) => ({
68
+ imageFiles: (filepayload.image_files ?? []).map((imagePayload) => ({
61
69
  fileLocation: imagePayload.file_location,
62
70
  height: imagePayload.height,
63
71
  signedUrl: (0, zeniUrl_1.toZeniUrl)(imagePayload.signed_url),
@@ -1,6 +1,7 @@
1
1
  import { AccountBasePayload } from '../account/accountPayload';
2
2
  import { AccountBase } from '../account/accountState';
3
3
  import { AddressPayload } from '../address/addressPayload';
4
+ import { FilePayload } from '../file/filePayload';
4
5
  export type InvoicingPriceType = 'tax_exclusive' | 'tax_inclusive';
5
6
  export declare const INVOICING_DUNNING_ACTIONS: readonly ["email_only", "email_and_retry", "do_nothing", "retry_only"];
6
7
  export type InvoicingDunningAction = (typeof INVOICING_DUNNING_ACTIONS)[number];
@@ -167,8 +168,20 @@ export interface InvoicingSettingsPayload {
167
168
  /** Empty string means hide the footer line; absent means use the default. */
168
169
  invoice_footer_text?: string;
169
170
  invoice_prefix?: string;
171
+ /**
172
+ * Read-only. The logo's Files record, whole, replacing the
173
+ * `logo_file_id` + `logo_url` pair the read used to return: those could
174
+ * disagree — most often an id whose file had been deleted — and neither
175
+ * carried the file's name. `null` means no logo, which is also how a stored
176
+ * id that no longer resolves comes back.
177
+ */
178
+ logo_file?: FilePayload | null;
179
+ /**
180
+ * Write-only. The save payload sets the logo by id; the read never returns it
181
+ * — see `logo_file`. Kept apart so a stale id cannot be mistaken for a logo
182
+ * that still exists.
183
+ */
170
184
  logo_file_id?: string;
171
- logo_url?: string;
172
185
  net_term_days?: number;
173
186
  payment_terms?: string;
174
187
  phone?: string;
@@ -41,6 +41,14 @@ export default user.reducer;
41
41
  /**
42
42
  * Helper functions.
43
43
  */
44
+ /**
45
+ * `image_file_ids` and `image_files` are typed as required, but a service that
46
+ * has no renditions to report can leave them out — the invoicing settings
47
+ * `logo_file` did. Mapping an absent `image_files` threw, taking down whichever
48
+ * reducer was mapping the file rather than losing one field, so both default to
49
+ * empty here: an absent list means no renditions, which is what an empty list
50
+ * already says.
51
+ */
44
52
  export function mapFilePayloadToFile(filepayload) {
45
53
  return {
46
54
  fileSizeUnit: filepayload.file_size_unit,
@@ -48,9 +56,9 @@ export function mapFilePayloadToFile(filepayload) {
48
56
  ? date(filepayload.update_time)
49
57
  : undefined,
50
58
  userId: filepayload.user_id ?? undefined,
51
- imageFileIds: filepayload.image_file_ids,
59
+ imageFileIds: filepayload.image_file_ids ?? [],
52
60
  documentSide: filepayload.document_side,
53
- imageFiles: filepayload.image_files.map((imagePayload) => ({
61
+ imageFiles: (filepayload.image_files ?? []).map((imagePayload) => ({
54
62
  fileLocation: imagePayload.file_location,
55
63
  height: imagePayload.height,
56
64
  signedUrl: toZeniUrl(imagePayload.signed_url),
@@ -36,7 +36,7 @@ export const getInvoicingCustomerRecord = (state, invoicingCustomerID) => {
36
36
  brandingAccentColor: settings?.accent_color,
37
37
  brandingCompanyName: settings?.company_name,
38
38
  brandingEmail: settings?.email,
39
- brandingLogoUrl: settings?.logo_url,
39
+ brandingLogoUrl: settings?.logo_file?.signed_url,
40
40
  stripeConnected: isStripeConnectedFromSettings(settings),
41
41
  fetchState: invoicingCustomerDetail?.fetchState ?? 'Not-Started',
42
42
  error: invoicingCustomerDetail?.error,
@@ -1,6 +1,7 @@
1
1
  import { of } from 'rxjs';
2
2
  import { catchError, filter, switchMap } from 'rxjs/operators';
3
3
  import { updateAddresses } from '../../../../entity/address/addressReducer';
4
+ import { updateFiles } from '../../../../entity/file/fileReducer';
4
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
6
  import { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, } from '../settingsViewReducer';
6
7
  export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSettings.match), switchMap(() => {
@@ -8,8 +9,13 @@ export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => action
8
9
  return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
9
10
  if (isSuccessResponse(response) && response.data != null) {
10
11
  const address = response.data.address;
12
+ // The logo record goes to the file entity, like the address goes to
13
+ // the address entity: the settings view keeps only its id and reads
14
+ // the file back through the file selector, so one copy is canonical.
15
+ const logoFile = response.data.logo_file;
11
16
  return [
12
17
  ...(address != null ? [updateAddresses([address])] : []),
18
+ ...(logoFile != null ? [updateFiles({ files: [logoFile] })] : []),
13
19
  updateInvoicingSettings(response.data),
14
20
  ];
15
21
  }
@@ -4,6 +4,7 @@ import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarA
4
4
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
6
  import { saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, } from '../settingsViewReducer';
7
+ import { updateFiles } from '../../../../entity/file/fileReducer';
7
8
  export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
8
9
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
9
10
  const { settings, snackbarSection } = action.payload;
@@ -22,7 +23,13 @@ export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions
22
23
  .putAndGetJSON(apiUrl, { ...settings })
23
24
  .pipe(switchMap((response) => {
24
25
  if (isSuccessResponse(response) && response.data != null) {
26
+ // Keep the file entity in step with what was persisted — the
27
+ // settings view resolves the logo by id, not from the response.
28
+ const savedLogoFile = response.data.logo_file;
25
29
  return from([
30
+ ...(savedLogoFile != null
31
+ ? [updateFiles({ files: [savedLogoFile] })]
32
+ : []),
26
33
  saveInvoicingSettingsSuccess(response.data),
27
34
  ...snackbar('success'),
28
35
  ]);
@@ -22,7 +22,7 @@ export const entityToInvoicingBrandingFormLocalData = (settings) => ({
22
22
  email: settings?.email ?? '',
23
23
  invoiceFooterText: settings?.invoice_footer_text ?? '',
24
24
  invoicePrefix: settings?.invoice_prefix ?? '',
25
- logoFileId: settings?.logo_file_id ?? '',
25
+ logoFileId: settings?.logo_file?.file_id ?? '',
26
26
  phone: settings?.phone ?? '',
27
27
  taxId: settings?.tax_id ?? '',
28
28
  });
@@ -1,4 +1,5 @@
1
1
  import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
2
+ import { getFileByFileId } from '../../../entity/file/fileSelector';
2
3
  import { getFormattedAddress } from '../../addressView/addressViewSelector';
3
4
  import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
4
5
  import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
@@ -25,10 +26,14 @@ export const getInvoicingSettingsFetchState = (state) => ({
25
26
  */
26
27
  export const getInvoicingSettingsView = (state) => {
27
28
  const { error, fetchState, saveFetchState, settings, stripeFetchState } = state.invoicingSettingsViewState;
29
+ const logoFileId = settings?.logo_file?.file_id ?? '';
28
30
  return {
29
31
  fieldHelp: invoicingFieldHelpForGroup(state, 'settings'),
30
32
  error,
31
33
  fetchState,
34
+ logoFile: logoFileId !== ''
35
+ ? getFileByFileId(state.fileState, logoFileId)
36
+ : undefined,
32
37
  saveState: saveFetchState,
33
38
  settings,
34
39
  stripeConnectState: stripeFetchState,
@@ -1,6 +1,7 @@
1
1
  import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateAddresses } from '../../../entity/address/addressReducer';
4
+ import { updateFiles } from '../../../entity/file/fileReducer';
4
5
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
5
6
  import { resetNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
6
7
  import { INVOICING_BUSINESS_ADDRESS_TYPE, brandingLocalDataToSettingsPayload, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
@@ -23,10 +24,18 @@ export const submitInvoicingBrandingFormEpic = (actions$, state$, zeniAPI) => ac
23
24
  .pipe(mergeMap((response) => {
24
25
  if (isSuccessResponse(response) && response.data != null) {
25
26
  const savedAddress = response.data.address;
27
+ // The saved logo goes to the file entity for the same reason the
28
+ // saved address goes to the address entity: the settings view
29
+ // resolves the file by id, so without this a save that changes
30
+ // the logo leaves the view resolving an id nothing holds.
31
+ const savedLogoFile = response.data.logo_file;
26
32
  return [
27
33
  ...(savedAddress != null
28
34
  ? [updateAddresses([savedAddress])]
29
35
  : []),
36
+ ...(savedLogoFile != null
37
+ ? [updateFiles({ files: [savedLogoFile] })]
38
+ : []),
30
39
  saveInvoicingSettingsSuccess(response.data),
31
40
  resetNewAddressDataInLocalStore(INVOICING_BUSINESS_ADDRESS_TYPE),
32
41
  ];
@@ -39,7 +39,7 @@ const getInvoicingCustomerRecord = (state, invoicingCustomerID) => {
39
39
  brandingAccentColor: settings?.accent_color,
40
40
  brandingCompanyName: settings?.company_name,
41
41
  brandingEmail: settings?.email,
42
- brandingLogoUrl: settings?.logo_url,
42
+ brandingLogoUrl: settings?.logo_file?.signed_url,
43
43
  stripeConnected: (0, invoicingSettingsHelpers_1.isStripeConnectedFromSettings)(settings),
44
44
  fetchState: invoicingCustomerDetail?.fetchState ?? 'Not-Started',
45
45
  error: invoicingCustomerDetail?.error,
@@ -4,6 +4,7 @@ exports.fetchInvoicingSettingsEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const addressReducer_1 = require("../../../../entity/address/addressReducer");
7
+ const fileReducer_1 = require("../../../../entity/file/fileReducer");
7
8
  const responsePayload_1 = require("../../../../responsePayload");
8
9
  const settingsViewReducer_1 = require("../settingsViewReducer");
9
10
  const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.fetchInvoicingSettings.match), (0, operators_1.switchMap)(() => {
@@ -11,8 +12,13 @@ const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe
11
12
  return zeniAPI.getJSON(apiUrl).pipe((0, operators_1.switchMap)((response) => {
12
13
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
13
14
  const address = response.data.address;
15
+ // The logo record goes to the file entity, like the address goes to
16
+ // the address entity: the settings view keeps only its id and reads
17
+ // the file back through the file selector, so one copy is canonical.
18
+ const logoFile = response.data.logo_file;
14
19
  return [
15
20
  ...(address != null ? [(0, addressReducer_1.updateAddresses)([address])] : []),
21
+ ...(logoFile != null ? [(0, fileReducer_1.updateFiles)({ files: [logoFile] })] : []),
16
22
  (0, settingsViewReducer_1.updateInvoicingSettings)(response.data),
17
23
  ];
18
24
  }
@@ -7,6 +7,7 @@ const snackbarApiError_1 = require("../../../../entity/snackbar/snackbarApiError
7
7
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
8
8
  const responsePayload_1 = require("../../../../responsePayload");
9
9
  const settingsViewReducer_1 = require("../settingsViewReducer");
10
+ const fileReducer_1 = require("../../../../entity/file/fileReducer");
10
11
  const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.saveInvoicingSettings.match), (0, operators_1.mergeMap)((action) => {
11
12
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
12
13
  const { settings, snackbarSection } = action.payload;
@@ -25,7 +26,13 @@ const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(
25
26
  .putAndGetJSON(apiUrl, { ...settings })
26
27
  .pipe((0, operators_1.switchMap)((response) => {
27
28
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
29
+ // Keep the file entity in step with what was persisted — the
30
+ // settings view resolves the logo by id, not from the response.
31
+ const savedLogoFile = response.data.logo_file;
28
32
  return (0, rxjs_1.from)([
33
+ ...(savedLogoFile != null
34
+ ? [(0, fileReducer_1.updateFiles)({ files: [savedLogoFile] })]
35
+ : []),
29
36
  (0, settingsViewReducer_1.saveInvoicingSettingsSuccess)(response.data),
30
37
  ...snackbar('success'),
31
38
  ]);
@@ -1,5 +1,6 @@
1
1
  import { updateAddresses } from '../../../../entity/address/addressReducer';
2
+ import { updateFiles } from '../../../../entity/file/fileReducer';
2
3
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
3
4
  import { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, submitInvoicingQboSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, updateInvoicingStripeConnection } from '../settingsViewReducer';
4
5
  /** Every action the settings epics accept or emit. */
5
- export type InvoicingSettingsViewActionType = ReturnType<typeof fetchInvoicingSettings> | ReturnType<typeof updateInvoicingSettings> | ReturnType<typeof updateInvoicingSettingsFailure> | ReturnType<typeof saveInvoicingSettings> | ReturnType<typeof saveInvoicingSettingsSuccess> | ReturnType<typeof submitInvoicingQboSettings> | ReturnType<typeof saveInvoicingSettingsFailure> | ReturnType<typeof connectInvoicingStripe> | ReturnType<typeof disconnectInvoicingStripe> | ReturnType<typeof updateInvoicingStripeConnection> | ReturnType<typeof invoicingStripeConnectionFailure> | ReturnType<typeof updateAddresses> | ReturnType<typeof openSnackbar>;
6
+ export type InvoicingSettingsViewActionType = ReturnType<typeof fetchInvoicingSettings> | ReturnType<typeof updateInvoicingSettings> | ReturnType<typeof updateInvoicingSettingsFailure> | ReturnType<typeof saveInvoicingSettings> | ReturnType<typeof saveInvoicingSettingsSuccess> | ReturnType<typeof submitInvoicingQboSettings> | ReturnType<typeof saveInvoicingSettingsFailure> | ReturnType<typeof connectInvoicingStripe> | ReturnType<typeof disconnectInvoicingStripe> | ReturnType<typeof updateInvoicingStripeConnection> | ReturnType<typeof invoicingStripeConnectionFailure> | ReturnType<typeof updateAddresses> | ReturnType<typeof updateFiles> | ReturnType<typeof openSnackbar>;
@@ -25,7 +25,7 @@ const entityToInvoicingBrandingFormLocalData = (settings) => ({
25
25
  email: settings?.email ?? '',
26
26
  invoiceFooterText: settings?.invoice_footer_text ?? '',
27
27
  invoicePrefix: settings?.invoice_prefix ?? '',
28
- logoFileId: settings?.logo_file_id ?? '',
28
+ logoFileId: settings?.logo_file?.file_id ?? '',
29
29
  phone: settings?.phone ?? '',
30
30
  taxId: settings?.tax_id ?? '',
31
31
  });
@@ -1,6 +1,7 @@
1
1
  import { NestedAccountHierarchyForReport } from '../../../commonStateTypes/accountView/nestedAccountID';
2
2
  import { FetchState, FetchStateAndError } from '../../../commonStateTypes/common';
3
3
  import { InvoicingDunningActionOption, InvoicingFieldHelpByCode, InvoicingNamedOption, InvoicingQboAccountMappingKindOption, InvoicingSettingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
4
+ import { File } from '../../../entity/file/fileState';
4
5
  import { ZeniAPIStatus } from '../../../responsePayload';
5
6
  import { RootState } from '../../../rootStateTypes';
6
7
  import { InvoicingQboConnection, InvoicingQboSyncHealth } from '../invoicingQboView/invoicingQboViewPayload';
@@ -24,6 +25,13 @@ export interface InvoicingSettingsView {
24
25
  saveState: FetchStateAndError;
25
26
  stripeConnectState: FetchStateAndError;
26
27
  error?: ZeniAPIStatus;
28
+ /**
29
+ * The tenant's logo, read back out of the file entity the settings fetch
30
+ * stored it in. `undefined` when no logo is set — the settings response
31
+ * returns no record then, which is also how a logo deleted out from under a
32
+ * stale id reads, so the screen has nothing to show either way.
33
+ */
34
+ logoFile?: File;
27
35
  settings?: InvoicingSettingsPayload;
28
36
  }
29
37
  /**
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getInvoicingBrandingFormView = exports.getInvoicingQboSettingsFormView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = void 0;
4
4
  exports.deriveInvoicingEnablePrerequisites = deriveInvoicingEnablePrerequisites;
5
5
  const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
6
+ const fileSelector_1 = require("../../../entity/file/fileSelector");
6
7
  const addressViewSelector_1 = require("../../addressView/addressViewSelector");
7
8
  const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
8
9
  const invoicingQboViewSelector_1 = require("../invoicingQboView/invoicingQboViewSelector");
@@ -31,10 +32,14 @@ exports.getInvoicingSettingsFetchState = getInvoicingSettingsFetchState;
31
32
  */
32
33
  const getInvoicingSettingsView = (state) => {
33
34
  const { error, fetchState, saveFetchState, settings, stripeFetchState } = state.invoicingSettingsViewState;
35
+ const logoFileId = settings?.logo_file?.file_id ?? '';
34
36
  return {
35
37
  fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'settings'),
36
38
  error,
37
39
  fetchState,
40
+ logoFile: logoFileId !== ''
41
+ ? (0, fileSelector_1.getFileByFileId)(state.fileState, logoFileId)
42
+ : undefined,
38
43
  saveState: saveFetchState,
39
44
  settings,
40
45
  stripeConnectState: stripeFetchState,
@@ -1,11 +1,12 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
2
  import { Observable } from 'rxjs';
3
3
  import { updateAddresses } from '../../../entity/address/addressReducer';
4
+ import { updateFiles } from '../../../entity/file/fileReducer';
4
5
  import { RootState } from '../../../rootStateTypes';
5
6
  import { ZeniAPI } from '../../../zeniAPI';
6
7
  import { resetNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
7
8
  import { saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, submitInvoicingBrandingForm } from './settingsViewReducer';
8
- export type SubmitActionType = ReturnType<typeof submitInvoicingBrandingForm> | ReturnType<typeof saveInvoicingSettingsSuccess> | ReturnType<typeof saveInvoicingSettingsFailure> | ReturnType<typeof resetNewAddressDataInLocalStore> | ReturnType<typeof updateAddresses>;
9
+ export type SubmitActionType = ReturnType<typeof submitInvoicingBrandingForm> | ReturnType<typeof saveInvoicingSettingsSuccess> | ReturnType<typeof saveInvoicingSettingsFailure> | ReturnType<typeof resetNewAddressDataInLocalStore> | ReturnType<typeof updateAddresses> | ReturnType<typeof updateFiles>;
9
10
  /**
10
11
  * On `submitInvoicingBrandingForm`, PUT the settings body built from the branding
11
12
  * draft + the business address captured via the shared Address screen. The
@@ -4,6 +4,7 @@ exports.submitInvoicingBrandingFormEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const addressReducer_1 = require("../../../entity/address/addressReducer");
7
+ const fileReducer_1 = require("../../../entity/file/fileReducer");
7
8
  const responsePayload_1 = require("../../../responsePayload");
8
9
  const addressViewReducer_1 = require("../../addressView/addressViewReducer");
9
10
  const invoicingBrandingFormConfig_1 = require("./invoicingBrandingFormConfig");
@@ -26,10 +27,18 @@ const submitInvoicingBrandingFormEpic = (actions$, state$, zeniAPI) => actions$.
26
27
  .pipe((0, operators_1.mergeMap)((response) => {
27
28
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
28
29
  const savedAddress = response.data.address;
30
+ // The saved logo goes to the file entity for the same reason the
31
+ // saved address goes to the address entity: the settings view
32
+ // resolves the file by id, so without this a save that changes
33
+ // the logo leaves the view resolving an id nothing holds.
34
+ const savedLogoFile = response.data.logo_file;
29
35
  return [
30
36
  ...(savedAddress != null
31
37
  ? [(0, addressReducer_1.updateAddresses)([savedAddress])]
32
38
  : []),
39
+ ...(savedLogoFile != null
40
+ ? [(0, fileReducer_1.updateFiles)({ files: [savedLogoFile] })]
41
+ : []),
33
42
  (0, settingsViewReducer_1.saveInvoicingSettingsSuccess)(response.data),
34
43
  (0, addressViewReducer_1.resetNewAddressDataInLocalStore)(invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE),
35
44
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.40",
3
+ "version": "5.2.41",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",