@zeniai/client-epic-state 5.3.18-betaAK2 → 5.3.18

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 (38) hide show
  1. package/lib/entity/tenant/epic/fetchAllTenantsEpic.d.ts +4 -2
  2. package/lib/entity/tenant/epic/fetchAllTenantsEpic.js +42 -45
  3. package/lib/entity/tenant/tenantPayload.d.ts +1 -17
  4. package/lib/entity/tenant/tenantReducer.d.ts +2 -7
  5. package/lib/entity/tenant/tenantReducer.js +19 -48
  6. package/lib/entity/tenant/tenantSelector.d.ts +6 -26
  7. package/lib/entity/tenant/tenantSelector.js +96 -119
  8. package/lib/entity/tenant/tenantState.d.ts +0 -10
  9. package/lib/epic.d.ts +1 -2
  10. package/lib/epic.js +0 -2
  11. package/lib/esm/entity/tenant/epic/fetchAllTenantsEpic.js +46 -49
  12. package/lib/esm/entity/tenant/tenantReducer.js +17 -46
  13. package/lib/esm/entity/tenant/tenantSelector.js +95 -115
  14. package/lib/esm/epic.js +0 -2
  15. package/lib/esm/index.js +7 -7
  16. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +3 -65
  17. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +0 -25
  18. package/lib/esm/view/dashboard/dashboardEpic.js +4 -0
  19. package/lib/esm/view/dashboard/dashboardSelector.js +3 -2
  20. package/lib/esm/view/financeStatement/financeStatementEpic.js +5 -1
  21. package/lib/esm/view/people/peoplePageEpic.js +4 -0
  22. package/lib/index.d.ts +8 -8
  23. package/lib/index.js +46 -56
  24. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +0 -20
  25. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +2 -2
  26. package/lib/view/aiCfoView/aiCfoViewReducer.js +4 -66
  27. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +1 -8
  28. package/lib/view/aiCfoView/aiCfoViewSelector.js +0 -27
  29. package/lib/view/aiCfoView/aiCfoViewState.d.ts +0 -9
  30. package/lib/view/dashboard/dashboardEpic.js +4 -0
  31. package/lib/view/dashboard/dashboardSelector.d.ts +2 -0
  32. package/lib/view/dashboard/dashboardSelector.js +2 -1
  33. package/lib/view/financeStatement/financeStatementEpic.js +5 -1
  34. package/lib/view/people/peoplePageEpic.js +4 -0
  35. package/package.json +1 -1
  36. package/lib/esm/view/aiCfoView/epics/transcribeAudioEpic.js +0 -60
  37. package/lib/view/aiCfoView/epics/transcribeAudioEpic.d.ts +0 -19
  38. package/lib/view/aiCfoView/epics/transcribeAudioEpic.js +0 -64
@@ -17,7 +17,6 @@ export const initialState = {
17
17
  error: undefined,
18
18
  accountLocked: false,
19
19
  tenantsById: {},
20
- tenantSummariesById: {},
21
20
  onboardingTenantsById: {},
22
21
  tenantIds: [],
23
22
  signInState: 'Not-Started',
@@ -108,24 +107,26 @@ const tenant = createSlice({
108
107
  });
109
108
  },
110
109
  },
111
- updateTenantSummariesSuccess(draft, action) {
110
+ updateTenantsSuccess(draft, action) {
111
+ const { tenants, user } = action.payload;
112
112
  draft.fetchState = 'Completed';
113
113
  draft.error = undefined;
114
- const tenantSummariesById = {};
115
- action.payload.forEach((payload) => {
116
- tenantSummariesById[payload.tenant_id] =
117
- mapTenantSummaryPayloadToTenantSummary(payload);
118
- });
119
- draft.tenantSummariesById = tenantSummariesById;
120
- draft.tenantIds = Object.keys(tenantSummariesById);
121
- // A tenant missing from the list is no longer accessible to the user.
122
- const accessibleTenantsById = {};
123
- Object.values(draft.tenantsById).forEach((tenant) => {
124
- if (tenantSummariesById[tenant.tenantId] != null) {
125
- accessibleTenantsById[tenant.tenantId] = tenant;
114
+ const [tenantsbyId, tenantIds, firstUser] = mapTenantsPayloadToState(tenants, user);
115
+ tenantIds.forEach((tenantId) => {
116
+ const existingTenant = draft.tenantsById[tenantId];
117
+ tenantsbyId[tenantId] = applyTreasuryPromoUserFieldsFromPayload(existingTenant, tenantsbyId[tenantId], user);
118
+ if (existingTenant != null) {
119
+ tenantsbyId[tenantId].capitalizableAccountOverrides =
120
+ tenantsbyId[tenantId].capitalizableAccountOverrides ??
121
+ existingTenant.capitalizableAccountOverrides;
122
+ tenantsbyId[tenantId].capitalizationOnboardingDismissedAccounts =
123
+ tenantsbyId[tenantId].capitalizationOnboardingDismissedAccounts ??
124
+ existingTenant.capitalizationOnboardingDismissedAccounts;
126
125
  }
127
126
  });
128
- draft.tenantsById = accessibleTenantsById;
127
+ draft.tenantsById = tenantsbyId;
128
+ draft.tenantIds = tenantIds;
129
+ draft.loggedInUser = assignWith(draft.loggedInUser ?? {}, firstUser, (objValue, srcValue) => srcValue ?? objValue);
129
130
  },
130
131
  updateTenantsFailure(draft, action) {
131
132
  draft.fetchState = 'Error';
@@ -133,7 +134,6 @@ const tenant = createSlice({
133
134
  draft.error = action.payload;
134
135
  draft.tenantIds = [];
135
136
  draft.tenantsById = {};
136
- draft.tenantSummariesById = {};
137
137
  },
138
138
  updateTenantSuccess(draft, action) {
139
139
  const { tenants, user } = action.payload;
@@ -242,7 +242,6 @@ const tenant = createSlice({
242
242
  draft.signInState = 'Error';
243
243
  draft.tenantIds = [];
244
244
  draft.tenantsById = {};
245
- draft.tenantSummariesById = {};
246
245
  },
247
246
  updateTenantReimbursementInfo(draft, action) {
248
247
  const { tenant_id, reimbursement_info } = action.payload;
@@ -272,24 +271,6 @@ const tenant = createSlice({
272
271
  updateCurrentTenant(draft, action) {
273
272
  draft.currentTenantId = action.payload;
274
273
  },
275
- /**
276
- * Records a tenant's ids known before the tenants list loads (the app's
277
- * cached tenant list), so `getCurrentTenantIds` has them from the first
278
- * render. The list is not changed and its own entries win.
279
- */
280
- seedTenantSummary(draft, action) {
281
- const { companyId, tenantEmailDomain, tenantId, tenantName } = action.payload;
282
- if (draft.tenantSummariesById[tenantId] == null) {
283
- draft.tenantSummariesById[tenantId] = {
284
- companyId,
285
- companyName: tenantName ?? '',
286
- tenantEmailDomain,
287
- tenantId,
288
- // Not in `tenantIds`, so it is never ordered or listed.
289
- userLastCheckInTime: date('1970-01-01'),
290
- };
291
- }
292
- },
293
274
  fetchExcludedResources: {
294
275
  prepare(userId, tenantId) {
295
276
  return { payload: { userId, tenantId } };
@@ -953,7 +934,7 @@ const tenant = createSlice({
953
934
  });
954
935
  },
955
936
  });
956
- export const { updateTenants, fetchAllTenants, updateTenantSummariesSuccess, seedTenantSummary, updateTenantsFailure, fetchActiveTenant, updateTenantSuccess, updateTenantFailure, updateCurrentTenant, fetchExcludedResources, updateExcludedResourcesSuccess, updateExcludedResourcesFailure, doSignIn, doMagicLinkSignIn, magicLinkSignInSuccess, magicLinkSignInFailure, sendEmailMagicLinkToUser, sendEmailMagicLinkToUserSuccess, sendEmailMagicLinkToUserFailure, updateSignInState, doSignOut, signOutSuccess, sendSessionHeartbeat, sessionHeartbeatSuccess, sessionHeartbeatFailure, updateLoggedInUser, fetchExternalConnections, saveExternalConnection, saveExternalConnectionSuccess, saveExternalConnectionFailure, fetchExternalConnectionsFailure, fetchExternalConnectionsSuccess, saveAPIKeyConnection, saveAPIKeyConnectionSuccess, saveAPIKeyConnectionFailure, saveOAuthConnection, saveOAuthConnectionSuccess, saveOAuthConnectionFailure, saveConnectorCredentials, saveConnectorCredentialsSuccess, saveConnectorCredentialsFailure, initEmailConnectOAuth, initEmailConnectOAuthSuccess, initEmailConnectOAuthFailure, deleteConnection, deleteConnectionSuccess, deleteConnectionFailure, fetchSubscriptionSummaryForTenant, updateSubscriptionSummaryForTenantSuccess, updateSubscriptionSummaryForTenantFailure, updateOnboardingTenants, removeOnboardingTenant, updateTenantReimbursementInfo, updateReferViewedForLoggedInUser, resetSignInState, trigger2FA, updateTenantAccountingClassesEnabled, updateTenantAccountingProjectsEnabled, updateTenantAutoReviewIncomeWithoutCustomer, updateTenantProductSettings, updateTenantCapitalizationOnboardingDismissed, updateTenantCapitalizationAccountOverride, updateTenantMasterTOSInfo, verifyDeviceWithTwoFA, verifyDeviceWithTwoFASuccess, verifyDeviceWithTwoFAFailure, updateTreasuryVideoViewedForLoggedInUser, updateTreasuryPromoRemindMeLaterClickedForLoggedInUser, updateTreasuryPromoIntroClosedByOutsideClickForLoggedInUser, resendVerifyDeviceOTP, resendVerifyDeviceOTPSuccess, resendVerifyDeviceOTPFailure, } = tenant.actions;
937
+ export const { updateTenants, fetchAllTenants, updateTenantsSuccess, updateTenantsFailure, fetchActiveTenant, updateTenantSuccess, updateTenantFailure, updateCurrentTenant, fetchExcludedResources, updateExcludedResourcesSuccess, updateExcludedResourcesFailure, doSignIn, doMagicLinkSignIn, magicLinkSignInSuccess, magicLinkSignInFailure, sendEmailMagicLinkToUser, sendEmailMagicLinkToUserSuccess, sendEmailMagicLinkToUserFailure, updateSignInState, doSignOut, signOutSuccess, sendSessionHeartbeat, sessionHeartbeatSuccess, sessionHeartbeatFailure, updateLoggedInUser, fetchExternalConnections, saveExternalConnection, saveExternalConnectionSuccess, saveExternalConnectionFailure, fetchExternalConnectionsFailure, fetchExternalConnectionsSuccess, saveAPIKeyConnection, saveAPIKeyConnectionSuccess, saveAPIKeyConnectionFailure, saveOAuthConnection, saveOAuthConnectionSuccess, saveOAuthConnectionFailure, saveConnectorCredentials, saveConnectorCredentialsSuccess, saveConnectorCredentialsFailure, initEmailConnectOAuth, initEmailConnectOAuthSuccess, initEmailConnectOAuthFailure, deleteConnection, deleteConnectionSuccess, deleteConnectionFailure, fetchSubscriptionSummaryForTenant, updateSubscriptionSummaryForTenantSuccess, updateSubscriptionSummaryForTenantFailure, updateOnboardingTenants, removeOnboardingTenant, updateTenantReimbursementInfo, updateReferViewedForLoggedInUser, resetSignInState, trigger2FA, updateTenantAccountingClassesEnabled, updateTenantAccountingProjectsEnabled, updateTenantAutoReviewIncomeWithoutCustomer, updateTenantProductSettings, updateTenantCapitalizationOnboardingDismissed, updateTenantCapitalizationAccountOverride, updateTenantMasterTOSInfo, verifyDeviceWithTwoFA, verifyDeviceWithTwoFASuccess, verifyDeviceWithTwoFAFailure, updateTreasuryVideoViewedForLoggedInUser, updateTreasuryPromoRemindMeLaterClickedForLoggedInUser, updateTreasuryPromoIntroClosedByOutsideClickForLoggedInUser, resendVerifyDeviceOTP, resendVerifyDeviceOTPSuccess, resendVerifyDeviceOTPFailure, } = tenant.actions;
957
938
  export default tenant.reducer;
958
939
  /**
959
940
  * Applies treasury promo user fields from the tenants API user block when present.
@@ -995,16 +976,6 @@ function mapTenantsPayloadToState(tenantsPayload, userTenantPayload) {
995
976
  const firstUser = mapTenantPayloadToUser(tenantsPayload[0]);
996
977
  return [tenantsById, tenantIds, firstUser];
997
978
  }
998
- function mapTenantSummaryPayloadToTenantSummary(payload) {
999
- return {
1000
- companyId: payload.company_id,
1001
- companyName: payload.company_name,
1002
- tenantEmailDomain: payload.tenant_email_domain,
1003
- tenantId: payload.tenant_id,
1004
- // A tenant never opened sorts last, as in `mapTenantPayloadToTenant`.
1005
- userLastCheckInTime: date(payload.user_last_check_in_time_into_tenant ?? '1970-01-01'),
1006
- };
1007
- }
1008
979
  function mapTenantPayloadToTenant(payload, userTenantPayload) {
1009
980
  const tenant = {
1010
981
  accountingClassIDs: payload.accounting_class_ids,
@@ -16,75 +16,50 @@ export function getTenant(tenantState, companyState, tenantId) {
16
16
  }
17
17
  }
18
18
  export function getTenantIdByCompanyId(tenantState, companyId) {
19
- return (Object.values(tenantState.tenantsById).find((tenant) => tenant.companyId === companyId) ??
20
- Object.values(tenantState.tenantSummariesById).find((tenantSummary) => tenantSummary.companyId === companyId))?.tenantId;
19
+ return Object.values(tenantState.tenantsById).find((tenant) => tenant.companyId === companyId)?.tenantId;
21
20
  }
22
- /**
23
- * Every tenant in the list, most recently opened first. Roles are known only
24
- * for tenants whose full record is loaded; the list itself carries none.
25
- */
26
- function getTenantListEntriesByCheckInDate(tenantState) {
27
- const entries = tenantState.tenantIds
28
- .map((tenantId) => {
29
- const tenant = tenantState.tenantsById[tenantId];
30
- const tenantSummary = tenantState.tenantSummariesById[tenantId];
31
- if (tenant != null) {
32
- return {
33
- companyId: tenant.companyId,
34
- companyName: tenant.companyName,
35
- tenantEmailDomain: tenant.tenantEmailDomain,
36
- tenantId: tenant.tenantId,
37
- userLastCheckInTime: tenant.userLastCheckInTime,
38
- userRole: tenant.userRole,
39
- };
40
- }
41
- return tenantSummary != null
42
- ? { ...tenantSummary, userRole: [] }
43
- : undefined;
44
- })
45
- .filter((entry) => entry != null);
46
- return orderBy(entries, (entry) => entry.userLastCheckInTime.valueOf(), 'desc');
47
- }
48
- /**
49
- * The selected tenant's full record. With no tenant selected, or one the user
50
- * can no longer open, it is the most recently opened loaded tenant. A selected
51
- * tenant that is in the list but not loaded yet reads as no tenant until its
52
- * fetch lands, so nothing renders another tenant's record in its place.
53
- */
54
- function getCurrentTenantView(tenantState, companyState) {
55
- const { currentTenantId } = tenantState;
56
- if (currentTenantId != null) {
57
- const tenant = getTenant(tenantState, companyState, currentTenantId);
58
- if (tenant != null ||
59
- tenantState.tenantSummariesById[currentTenantId] != null) {
60
- return tenant;
61
- }
62
- }
63
- const mostRecentLoadedTenant = getTenantListEntriesByCheckInDate(tenantState).find((entry) => tenantState.tenantsById[entry.tenantId] != null);
64
- return mostRecentLoadedTenant != null
65
- ? getTenant(tenantState, companyState, mostRecentLoadedTenant.tenantId)
66
- : undefined;
21
+ function getTenantsByCheckInDate(tenantState, companyState) {
22
+ const tenants = tenantState.tenantIds
23
+ .map((tenantId) => getTenant(tenantState, companyState, tenantId))
24
+ .filter((value) => value != null);
25
+ const tenantsOrdered = orderBy(tenants, (value) => value.userLastCheckInTime.valueOf(), 'desc');
26
+ return {
27
+ order: 'descending_last_check_in',
28
+ tenantsInOrder: tenantsOrdered,
29
+ };
67
30
  }
68
- /** Whether the tenant's full record is loaded, not just its list entry. */
69
- export const isTenantLoaded = (state, tenantId) => state.tenantState.tenantsById[tenantId] != null;
70
- /**
71
- * Whether the selected tenant's full record is loaded. Screens that act on
72
- * roles or product settings wait for this rather than for either tenant fetch
73
- * to complete: the tenants list alone carries neither.
74
- */
75
- export const isCurrentTenantLoaded = (state) => {
76
- const { currentTenantId } = state.tenantState;
77
- return currentTenantId != null && isTenantLoaded(state, currentTenantId);
78
- };
79
31
  export const getTenantBaseViewForTenantId = createSelector((state) => state.tenantState, (state) => state.companyState, (_state, tenantId) => tenantId, (tenantState, companyState, tenantId) => {
80
- const entry = getTenantListEntriesByCheckInDate(tenantState).find((listEntry) => listEntry.tenantId === tenantId);
81
- return entry != null
82
- ? getTenantBaseViewForEntry(entry, companyState)
83
- : undefined;
32
+ const tenant = tenantState.tenantsById[tenantId];
33
+ if (tenant != null) {
34
+ const companyId = tenant.companyId;
35
+ const companyName = companyState.companiesById[companyId]?.companyInfo.name;
36
+ const tenantName = tenant.companyName ?? companyName;
37
+ const tenantEmailDomain = tenant.tenantEmailDomain;
38
+ const userRole = tenant.userRole;
39
+ const companyLocaleInfo = companyState.companiesById[companyId]?.companyLocaleInfo;
40
+ return {
41
+ tenantId,
42
+ tenantName,
43
+ tenantEmailDomain,
44
+ companyId,
45
+ companyName,
46
+ userRole,
47
+ companyLocaleInfo,
48
+ };
49
+ }
50
+ return undefined;
84
51
  });
85
52
  const getCurrentTenantHelper = (tenantState, companyState) => {
86
- const { currentTenantId, excludedResourcesByTenant, subscriptionSummaryByTenant, } = tenantState;
87
- const tenant = getCurrentTenantView(tenantState, companyState);
53
+ const { tenantIds, currentTenantId, excludedResourcesByTenant, subscriptionSummaryByTenant, } = tenantState;
54
+ let tenant = undefined;
55
+ if (currentTenantId != null) {
56
+ tenant = getTenant(tenantState, companyState, currentTenantId);
57
+ }
58
+ if (tenant == null && tenantIds.length > 0) {
59
+ const tenants = getTenantsByCheckInDate(tenantState, companyState);
60
+ tenant =
61
+ tenants.tenantsInOrder.length > 0 ? tenants.tenantsInOrder[0] : undefined;
62
+ }
88
63
  let excludedResources = undefined;
89
64
  if (tenant != null) {
90
65
  excludedResources = excludedResourcesByTenant[tenant.tenantId];
@@ -145,34 +120,9 @@ const getCurrentTenantHelper = (tenantState, companyState) => {
145
120
  export const getCurrentTenant = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
146
121
  return getCurrentTenantHelper(tenantState, companyState);
147
122
  });
148
- /**
149
- * The selected tenant's id as soon as it is chosen. Unlike `getCurrentTenant`,
150
- * it is set before the tenant fetch lands (boot reads it from the cached
151
- * tenant list).
152
- */
153
- export const getCurrentTenantId = (state) => state.tenantState.currentTenantId;
154
- const getCurrentTenantIdValues = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
155
- const tenant = getCurrentTenantView(tenantState, companyState);
156
- if (tenant != null) {
157
- return { tenantId: tenant.tenantId, companyId: tenant.companyId };
158
- }
159
- const { currentTenantId } = tenantState;
160
- const tenantSummary = currentTenantId != null
161
- ? tenantState.tenantSummariesById[currentTenantId]
162
- : undefined;
163
- return {
164
- tenantId: tenantSummary?.tenantId,
165
- companyId: tenantSummary?.companyId,
166
- };
123
+ export const getTenantsByCheckInDateSelector = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
124
+ return getTenantsByCheckInDate(tenantState, companyState);
167
125
  });
168
- /**
169
- * The current tenant's ids before its record loads: from the record when
170
- * loaded, otherwise from the tenants list or the entry seeded from the app's
171
- * cache (`seedTenantSummary`). Providers keyed on them (Statsig, collaboration)
172
- * then mount once with the final ids, on a reload and on a tenant switch.
173
- * The result keeps its identity while the ids are unchanged.
174
- */
175
- export const getCurrentTenantIds = createSelector((state) => getCurrentTenantIdValues(state).tenantId, (state) => getCurrentTenantIdValues(state).companyId, (tenantId, companyId) => ({ tenantId, companyId }));
176
126
  export function getOnboardingTenantByTenantId(tenantState, tenantId) {
177
127
  const tenant = tenantState.onboardingTenantsById[tenantId];
178
128
  if (tenant != null) {
@@ -183,7 +133,10 @@ export function getOnboardingTenantByTenantId(tenantState, tenantId) {
183
133
  }
184
134
  }
185
135
  export const getTenantBaseById = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
186
- return getTenantListEntriesByCheckInDate(tenantState).map((entry) => getTenantBaseViewForEntry(entry, companyState));
136
+ return tenantState.tenantIds.map((tenantId) => {
137
+ const tenant = tenantState.tenantsById[tenantId];
138
+ return getTenantBaseViewForTenant(tenant, companyState);
139
+ });
187
140
  });
188
141
  export const getTenantsBaseByCheckInDateSelector = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
189
142
  return getTenantsBaseByCheckInDate(tenantState, companyState);
@@ -212,8 +165,16 @@ export const isTenantUsingZeniCOA = (currentTenant) => {
212
165
  return currentTenant.company?.companyInfo.isZeniCOAInUse ?? true; // defaulting to true
213
166
  };
214
167
  export const getCurrentTenantExternalConnections = (tenantState, companyState) => {
215
- const { externalConnectionsByTenant, currentTenantId } = tenantState;
216
- const tenant = getCurrentTenantView(tenantState, companyState);
168
+ const { externalConnectionsByTenant, tenantIds, currentTenantId } = tenantState;
169
+ let tenant = undefined;
170
+ if (currentTenantId != null) {
171
+ tenant = getTenant(tenantState, companyState, currentTenantId);
172
+ }
173
+ if (tenant == null && tenantIds.length > 0) {
174
+ const tenants = getTenantsByCheckInDate(tenantState, companyState);
175
+ tenant =
176
+ tenants.tenantsInOrder.length > 0 ? tenants.tenantsInOrder[0] : undefined;
177
+ }
217
178
  let externalConnections = undefined;
218
179
  if (tenant != null) {
219
180
  externalConnections = externalConnectionsByTenant[tenant.tenantId];
@@ -227,39 +188,58 @@ export const getCurrentTenantExternalConnections = (tenantState, companyState) =
227
188
  * Helpers
228
189
  */
229
190
  function getTenantsBaseByCheckInDate(tenantState, companyState) {
191
+ const tenants = tenantState.tenantIds
192
+ .map((tenantId) => getTenant(tenantState, companyState, tenantId))
193
+ .filter((value) => value != null);
194
+ const tenantsOrdered = orderBy(tenants, (value) => value.userLastCheckInTime.valueOf(), 'desc');
195
+ const tenantsBaseOrdered = tenantsOrdered.map((tenant) => getTenantBaseViewForTenant(tenant, companyState));
230
196
  return {
231
197
  order: 'descending_last_check_in',
232
- tenantsBaseInOrder: getTenantListEntriesByCheckInDate(tenantState).map((entry) => getTenantBaseViewForEntry(entry, companyState)),
198
+ tenantsBaseInOrder: tenantsBaseOrdered,
233
199
  };
234
200
  }
235
- const getTenantBaseViewForEntry = (entry, companyState) => {
236
- const company = companyState.companiesById[entry.companyId];
201
+ const getTenantBaseViewForTenant = (tenant, companyState) => {
202
+ const tenantId = tenant.tenantId;
203
+ const companyId = tenant.companyId;
204
+ const companyName = companyState.companiesById[companyId]?.companyInfo.name;
205
+ const tenantName = tenant.companyName ?? companyName ?? tenant.tenantEmailDomain ?? '';
206
+ const tenantEmailDomain = tenant.tenantEmailDomain;
207
+ const userRole = tenant.userRole;
208
+ const companyLocaleInfo = companyState.companiesById[companyId]?.companyLocaleInfo;
237
209
  return {
238
- tenantId: entry.tenantId,
239
- tenantName: entry.companyName ??
240
- company?.companyInfo.name ??
241
- entry.tenantEmailDomain ??
242
- '',
243
- tenantEmailDomain: entry.tenantEmailDomain,
244
- companyId: entry.companyId,
245
- // Companies are loaded only for opened tenants; the list carries the name.
246
- companyName: company?.companyInfo.name ?? entry.companyName,
247
- userRole: entry.userRole,
248
- companyLocaleInfo: company?.companyLocaleInfo,
210
+ tenantId,
211
+ tenantName,
212
+ tenantEmailDomain,
213
+ companyId,
214
+ companyName,
215
+ userRole,
216
+ companyLocaleInfo,
249
217
  };
250
218
  };
251
219
  function getTenantCoreDetailsByCheckInDate(tenantState, companyState) {
220
+ const tenants = tenantState.tenantIds
221
+ .map((tenantId) => getTenant(tenantState, companyState, tenantId))
222
+ .filter((value) => value != null);
223
+ const tenantsOrdered = orderBy(tenants, (value) => value.userLastCheckInTime.valueOf(), 'desc');
224
+ const tenantsCoreDetailsOrdered = tenantsOrdered.map((tenant) => getTenantCoreDetailsViewForTenant(tenant, companyState));
252
225
  return {
253
226
  order: 'descending_last_check_in',
254
- tenantsCoreDetailsInOrder: getTenantListEntriesByCheckInDate(tenantState).map((entry) => ({
255
- tenantId: entry.tenantId,
256
- tenantName: entry.companyName ??
257
- companyState.companiesById[entry.companyId]?.companyInfo.name,
258
- tenantEmailDomain: entry.tenantEmailDomain,
259
- companyId: entry.companyId,
260
- })),
227
+ tenantsCoreDetailsInOrder: tenantsCoreDetailsOrdered,
261
228
  };
262
229
  }
230
+ const getTenantCoreDetailsViewForTenant = (tenant, companyState) => {
231
+ const tenantId = tenant.tenantId;
232
+ const companyId = tenant.companyId;
233
+ const companyName = companyState.companiesById[companyId]?.companyInfo.name;
234
+ const tenantName = tenant.companyName ?? companyName;
235
+ const tenantEmailDomain = tenant.tenantEmailDomain;
236
+ return {
237
+ tenantId,
238
+ tenantName,
239
+ tenantEmailDomain,
240
+ companyId,
241
+ };
242
+ };
263
243
  export const toTenantCoreDetailsView = (tenantsBaseView) => {
264
244
  return tenantsBaseView.map(({ tenantId, tenantName, tenantEmailDomain, companyId }) => {
265
245
  return {
package/lib/esm/epic.js CHANGED
@@ -70,7 +70,6 @@ import { stopSubmitEpic, } from './view/aiCfoView/epics/stopSubmitEpic';
70
70
  import { stopSubmitQuestionEpic, } from './view/aiCfoView/epics/stopSubmitQuestionEpic';
71
71
  import { submitFeedbackEpic, } from './view/aiCfoView/epics/submitFeedbackEpic';
72
72
  import { submitQuestionEpic, } from './view/aiCfoView/epics/submitQuestionEpic';
73
- import { transcribeAudioEpic, } from './view/aiCfoView/epics/transcribeAudioEpic';
74
73
  import { fetchApAgingDetailEpic, } from './view/apAgingView/apAgingDetail/fetchApAgingDetailEpic';
75
74
  import { fetchApAgingEpic, } from './view/apAgingView/apAgingReport/fetchApAgingEpic';
76
75
  import { fetchArAgingDetailEpic, } from './view/arAgingView/arAgingDetail/fetchArAgingDetailEpic';
@@ -1485,7 +1484,6 @@ const ALL_EPICS = [
1485
1484
  syncInvoicingSubscriptionCouponsEpic,
1486
1485
  syncTabsAfterAutomatchEpic,
1487
1486
  toggleReportUIOptionForecastModeEpic,
1488
- transcribeAudioEpic,
1489
1487
  transferMoneyEpic,
1490
1488
  treasuryTransferMoneyEpic,
1491
1489
  triggerAiAccountantJobEpic,
package/lib/esm/index.js CHANGED
@@ -72,8 +72,8 @@ import { closeSnackbar, openSnackbar } from './entity/snackbar/snackbarReducer';
72
72
  import { getSnackbar } from './entity/snackbar/snackbarSelector';
73
73
  import { ALL_WEEK_DAYS, toDayOfWeek, toPriorityCodeType, toTaskStatusCodeType, } from './entity/task/taskState';
74
74
  import { getTaskGroupById } from './entity/taskGroup/taskGroupSelector';
75
- import { deleteConnection, doMagicLinkSignIn, doSignIn, doSignOut, fetchActiveTenant, fetchAllTenants, fetchExcludedResources as fetchExcludedResourcesForTenant, fetchExternalConnections as fetchExternalConnectionsForTenant, fetchSubscriptionSummaryForTenant, initEmailConnectOAuth, resendVerifyDeviceOTP, resetSignInState, saveAPIKeyConnection, saveConnectorCredentials, saveExternalConnection as saveExternalConnectionForTenant, saveOAuthConnection, seedTenantSummary, sendEmailMagicLinkToUser, sendSessionHeartbeat, toExternalIntegrationType, updateCurrentTenant, updateSignInState, verifyDeviceWithTwoFA, } from './entity/tenant/tenantReducer';
76
- import { DEFAULT_RECEIPTS_EMAIL, getCurrentTenant, getCurrentTenantId, getCurrentTenantIds, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isCurrentTenantLoaded, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantLoaded, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
75
+ import { deleteConnection, doMagicLinkSignIn, doSignIn, doSignOut, fetchActiveTenant, fetchAllTenants, fetchExcludedResources as fetchExcludedResourcesForTenant, fetchExternalConnections as fetchExternalConnectionsForTenant, fetchSubscriptionSummaryForTenant, initEmailConnectOAuth, resendVerifyDeviceOTP, resetSignInState, saveAPIKeyConnection, saveConnectorCredentials, saveExternalConnection as saveExternalConnectionForTenant, saveOAuthConnection, sendEmailMagicLinkToUser, sendSessionHeartbeat, toExternalIntegrationType, updateCurrentTenant, updateSignInState, verifyDeviceWithTwoFA, } from './entity/tenant/tenantReducer';
76
+ import { DEFAULT_RECEIPTS_EMAIL, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
77
77
  import { toExternalSupportedTool } from './entity/tenant/tenantTypes';
78
78
  import { pushToastNotification } from './entity/toastNotification/toastNotificationReducer';
79
79
  import { getLastNotificationTime, getNotifications, } from './entity/toastNotification/toastNotificationSelector';
@@ -332,7 +332,7 @@ import { clearInternationalWire, createPaymentInstrument, createPaymentInstrumen
332
332
  import { getInternationalWireView, } from './view/spendManagement/billPay/internationalWire/internationalWireSelector';
333
333
  import { fetchInternationalVerificationForm, submitInternationalVerificationForm, updateVerificationFormLocalData, } from './view/spendManagement/billPay/internationalWireVerification/internationalWireVerificationReducer';
334
334
  import { getIntlWireVerificationView, } from './view/spendManagement/billPay/internationalWireVerification/internationalWireVerificationSelector';
335
- import { fetchBillAttachment, fetchMagicLinkBankNameByRouting, fetchMagicLinkBankNameBySwift, fetchMagicLinkTenant, saveBankAccount, saveMagicLinkAddressInLocalStore, updateMagicLinkBankAccountLocalStoreData, updateMagicLinkInternationalBankAccountLocalStoreData, updateMagicLinkTenantFailure, updateMagicLinkTenantSuccess, } from './view/spendManagement/billPay/magicLinkView/magicLinkViewReducer';
335
+ import { fetchBillAttachment, fetchMagicLinkBankNameByRouting, fetchMagicLinkBankNameBySwift, fetchMagicLinkTenant, saveBankAccount, saveMagicLinkAddressInLocalStore, updateMagicLinkBankAccountLocalStoreData, updateMagicLinkInternationalBankAccountLocalStoreData, } from './view/spendManagement/billPay/magicLinkView/magicLinkViewReducer';
336
336
  import { getMagicLinkBankAccountView, getMagicLinkCurrentAddressState, getMagicLinkView, } from './view/spendManagement/billPay/magicLinkView/magicLinkViewSelector';
337
337
  import { fetchWiseRedirectStatus } from './view/spendManagement/billPay/wiseRedirectView/wiseRedirectReducer';
338
338
  import { getWiseRedirect } from './view/spendManagement/billPay/wiseRedirectView/wiseRedirectSelector';
@@ -509,7 +509,7 @@ export { toTimeframeTick, mapTimePeriodtoTimeframeTick, toTimePeriod, toAbsolute
509
509
  export { toVendorSpendTrendFilterTabsTypeStrict, } from './entity/vendorExpense/vendorSpendTrendFilterTabType';
510
510
  export { getNumberOfPeriods };
511
511
  export { SCHEDULE_DAYS_OF_MONTH, toScheduleDaysOfMonth, toMonth, toMonthStrict, toQuarter, toQuarterStrict, };
512
- export { getTenantBaseById, getCurrentTenant, getCurrentTenantId, getCurrentTenantIds, isCurrentTenantLoaded, isTenantLoaded, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, seedTenantSummary, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
512
+ export { getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
513
513
  export { toAccountType, toAccountGroupType, getAccountGroupKey, getAllAccounts, getTransactionFilterAccountOptions, };
514
514
  export { getAllClasses, getClassById, getClassFilterOptions, } from './entity/class/classSelector';
515
515
  export { getForecast };
@@ -661,7 +661,7 @@ export { isZeniClearingAccountReport, ZENI_CLEARING_ACCOUNT, isZeniClearingAccou
661
661
  export { fetchOnboardingCustomerView, fetchOnboardingCustomerSetupView, getOnboardingPlaidLinkToken, establishOnboardingPlaidConnection, updateOnboardingCustomerViewAccountDetails, updateOnboardingCustomerViewCompleteStatus, updateOnboardingCustomerViewDashboardLoaded, updateOnboardingPaymentAccountLoginStatus, updateOnboardingPaymentAccountStatus, updateOnboardingCustomerView, updateOnboardingCustomerViewLocalStoreData, updateCurrentStep, updateCurrentSubStep, updateOnboardingCustomerViewUIState, saveOnboardingCompnayOfficerPhoneInLocalStore, saveOnboardingCustomerViewDataInLocalStore, clearOnboardingCustomerViewDataInLocalStore, applyKycDocumentAutofillForOnboarding, applyKybDocumentAutofillForOnboarding, clearKycKybAutofillForOnboarding, acknowledgeOnboardingAiActivationViewed, acknowledgeOnboardingAiFinanceTeam, fetchAiAgentsActivationStatus, aiAgentsActivationCountsUpdated, aiAgentsActivationPhaseChanged, getAiAgentsActivationCounts, getAiAgentsActivationPhases, getAiAgentsActivationSubview, getOnboardingCustomerView, getProductSettingsString, ALL_ONBOARDING_STEPS, ALL_ONBOARDING_SUB_STEPS, toOnboardingStepType, toOnboardingSubStepType, };
662
662
  export { saveAccountMapping, saveAccountMappingLocalData, clearAccountMappingLocalData, getAccountMappingView, initializeAccountMappingView, };
663
663
  export { getMagicLinkView, getMagicLinkBankAccountView, getMagicLinkCurrentAddressState, };
664
- export { fetchMagicLinkTenant, updateMagicLinkTenantSuccess, updateMagicLinkTenantFailure, fetchBillAttachment, saveBankAccount, updateMagicLinkBankAccountLocalStoreData, updateMagicLinkInternationalBankAccountLocalStoreData, fetchMagicLinkBankNameByRouting, fetchMagicLinkBankNameBySwift, saveMagicLinkAddressInLocalStore, };
664
+ export { fetchMagicLinkTenant, fetchBillAttachment, saveBankAccount, updateMagicLinkBankAccountLocalStoreData, updateMagicLinkInternationalBankAccountLocalStoreData, fetchMagicLinkBankNameByRouting, fetchMagicLinkBankNameBySwift, saveMagicLinkAddressInLocalStore, };
665
665
  export { getWiseRedirect, fetchWiseRedirectStatus };
666
666
  export { getInternationalWireView, initializeInternationalWireLocalData, updateInternationalWireLocalStoreData, initializeDynamicForm, fetchInternationalWireDynamicForm, createPaymentInstrument, createPaymentInstrumentUpdateStatus, deletePaymentInstrument, clearInternationalWire, };
667
667
  export { fetchUserRoleConfig, getUserRoleConfig };
@@ -705,8 +705,8 @@ export { updateAiAccountantCustomers, updateAiAccountantCustomer, updateAiAccoun
705
705
  export { getAiAccountantCustomers, getAiAccountantJobsByTenantId, } from './entity/aiAccountantCustomer/aiAccountantCustomerSelector';
706
706
  export { cancelAiAccountantOnboarding, fetchAiAccountantCustomers, fetchAiAccountantJobs, setSelectedTenantIdsForJobTrigger, triggerAiAccountantJob, updateAiAccountantUIState, } from './view/aiAccountantView/aiAccountantViewReducer';
707
707
  export { getAiAccountantCockpitView, } from './view/aiAccountantView/aiAccountantViewSelector';
708
- export { acceptMasterTOS, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, clearSkillDetail, clearSkillMutation, closeRoutineRuns, closeRoutinesBrowser, closeSkillsBrowser, createSession, createSessionAndSubmit, deleteChatSession, deleteRoutine, deleteSkill, fetchChatHistory, fetchChatSessionsForUser, fetchRoutineRuns, fetchRoutines, fetchSkillDetail, fetchSkills, fetchSkillsFailure, fetchSkillsSuccess, fetchSkillVersionBody, fetchSkillVersions, fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess, openRoutinesBrowser, openSkillsBrowser, runRoutine, saveRoutine, saveSkill, setRoutineEnabled, setSession, setSkillVisibility, stopSubmit, stopSubmitQuestion, submitFeedback, submitQuestion, transcribeAudio, updateAiCfoViewScrollPosition, updateCotCollapsedState, updateCurrentInput, updateResponseState, } from './view/aiCfoView/aiCfoViewReducer';
709
- export { canCreateRoutine, getAiCfoView, getIsTranscribing, getRoutineMutation, getRoutineRuns, getRoutines, getRoutinesEnabled, getSkillDetail, getSkillMutation, getSkills, getSkillsEnabled, getSkillVersionBody, getSkillVersions, getSuggestedQuestionsForPageContext, getTranscriptionFailed, } from './view/aiCfoView/aiCfoViewSelector';
708
+ export { acceptMasterTOS, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, clearSkillDetail, clearSkillMutation, closeRoutineRuns, closeRoutinesBrowser, closeSkillsBrowser, createSession, createSessionAndSubmit, deleteChatSession, deleteRoutine, deleteSkill, fetchChatHistory, fetchChatSessionsForUser, fetchRoutineRuns, fetchRoutines, fetchSkillDetail, fetchSkills, fetchSkillsFailure, fetchSkillsSuccess, fetchSkillVersionBody, fetchSkillVersions, fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess, openRoutinesBrowser, openSkillsBrowser, runRoutine, saveRoutine, saveSkill, setRoutineEnabled, setSession, setSkillVisibility, stopSubmit, stopSubmitQuestion, submitFeedback, submitQuestion, updateAiCfoViewScrollPosition, updateCotCollapsedState, updateCurrentInput, updateResponseState, } from './view/aiCfoView/aiCfoViewReducer';
709
+ export { canCreateRoutine, getAiCfoView, getRoutineMutation, getRoutineRuns, getRoutines, getRoutinesEnabled, getSkillDetail, getSkillMutation, getSkills, getSkillsEnabled, getSkillVersionBody, getSkillVersions, getSuggestedQuestionsForPageContext, } from './view/aiCfoView/aiCfoViewSelector';
710
710
  export { setNewSession, setSessions, updateAiCfoAnswerCardPolicyWizardPlan, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearSession, toAiCfoExports, toAiCfoVisualization, appendSyntheticAiCfoAnswer, updateSyntheticAiCfoAnswer, removeSyntheticAiCfoAnswer, } from './entity/aiCfo/aiCfoReducer';
711
711
  export { getAllQuestionAnswersForChatSession, getQuestionAnswerByIdForChatSession, getAllQuestionsForChatSession, getAiCfoSelectorView, getSyntheticAiCfoAnswersForChatSession, getSyntheticAiCfoAnswerByQuestionAnswerId, } from './entity/aiCfo/aiCfoSelector';
712
712
  export { ALL_AI_CFO_CHARTS_TYPES, ALL_AI_CFO_VISUALIZATION_TYPES, ALL_AI_CFO_ANSWER_STATE_TYPES, ALL_AI_CFO_ANSWER_RESPONSE_TYPES, ALL_INTERACTIVE_FORM_TYPES, toAiCfoVisualizationType, toAiCfoVisualizationTypeStrict, toAiCfoChartType, toAiCfoChartTypeStrict, toAiCfoAnswerStateType, toAiCfoAnswerStateTypeStrict, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toInteractiveFormType, toInteractiveFormTypeStrict, ALL_SYNTHETIC_AI_CFO_ANSWER_KINDS, toMessageSender, toMessageType, } from './entity/aiCfo/aiCfoState';
@@ -47,8 +47,6 @@ export const initialAiCfoViewState = {
47
47
  fetchChatSessionHistoryByChatSessionId: {},
48
48
  responseStateByChatSessionId: {},
49
49
  stopSubmitStateByChatSessionId: {},
50
- transcriptionState: { fetchState: 'Not-Started', error: undefined },
51
- transcribingChatSessionId: undefined,
52
50
  suggestedQuestionsByPageContext: {},
53
51
  skillDetail: {
54
52
  fetchState: 'Not-Started',
@@ -149,19 +147,6 @@ function closeSkillsBrowserState(draft) {
149
147
  draft.uiState.isSkillsBrowserOpen = false;
150
148
  clearOpenSkillState(draft);
151
149
  }
152
- /**
153
- * Clear a finished dictation attempt when the conversation moves on, so a failed one
154
- * stops showing "Could not transcribe that recording" in every later chat. A running
155
- * take keeps its state — the composer is showing a live "Transcribing…" for it.
156
- */
157
- function resetFinishedTranscription(draft) {
158
- if (draft.transcriptionState.fetchState === 'In-Progress') {
159
- return;
160
- }
161
- draft.transcriptionState.fetchState = 'Not-Started';
162
- draft.transcriptionState.error = undefined;
163
- draft.transcribingChatSessionId = undefined;
164
- }
165
150
  const aiCfoView = createSlice({
166
151
  name: 'aiCfoView',
167
152
  initialState: initialAiCfoViewState,
@@ -169,7 +154,6 @@ const aiCfoView = createSlice({
169
154
  submitQuestion: {
170
155
  reducer(draft, action) {
171
156
  const { sessionId } = action.payload;
172
- resetFinishedTranscription(draft);
173
157
  // A turn is being sent, so the chat body has to be what the user sees. Doing this
174
158
  // here rather than at each call site is deliberate: the composer, the sample
175
159
  // chips, Run, and the `/` menu all submit, and every one of them was a separate
@@ -315,7 +299,6 @@ const aiCfoView = createSlice({
315
299
  setSession(draft, action) {
316
300
  draft.uiState.currentChatSessionId = action.payload.sessionId;
317
301
  draft.uiState.agentId = action.payload.agentId;
318
- resetFinishedTranscription(draft);
319
302
  // Picking a session means wanting to see that chat, so the browser cannot stay over
320
303
  // it. In the reducer because there are six dispatch sites across three separate
321
304
  // rails, and covering them one at a time missed half of them.
@@ -368,7 +351,6 @@ const aiCfoView = createSlice({
368
351
  createSessionAndSubmit: {
369
352
  reducer(draft, action) {
370
353
  draft.uiState.agentId = action.payload.agentId;
371
- resetFinishedTranscription(draft);
372
354
  draft.createSessionAndSubmitState.fetchState = 'In-Progress';
373
355
  draft.createSessionAndSubmitState.error = undefined;
374
356
  },
@@ -569,49 +551,7 @@ const aiCfoView = createSlice({
569
551
  return { payload: { chatMessageId, error } };
570
552
  },
571
553
  },
572
- transcribeAudio: {
573
- reducer(draft, action) {
574
- draft.transcriptionState.fetchState = 'In-Progress';
575
- draft.transcriptionState.error = undefined;
576
- draft.transcribingChatSessionId = action.payload.chatSessionId;
577
- },
578
- prepare(audio, language, chatSessionId) {
579
- return { payload: { audio, chatSessionId, language } };
580
- },
581
- },
582
- transcribeAudioSuccess: {
583
- reducer(draft, action) {
584
- draft.transcriptionState.fetchState = 'Completed';
585
- draft.transcriptionState.error = undefined;
586
- // The user moved to another session while this was in flight — dropping it
587
- // is the only safe option, since appending would put their words in a
588
- // conversation they did not dictate into.
589
- if (action.payload.chatSessionId !== draft.uiState.currentChatSessionId) {
590
- return;
591
- }
592
- const transcript = action.payload.transcript.trim();
593
- if (transcript === '') {
594
- // Silence, not a failure: leave the composer untouched so the user can
595
- // simply speak again rather than hunting for what went wrong.
596
- return;
597
- }
598
- // Append to whatever is in the box now rather than to a snapshot taken when
599
- // recording began — a take produces exactly one result, so there is nothing
600
- // to reconcile, and anything typed while transcribing survives.
601
- const existing = draft.uiState.currentInput.trim();
602
- draft.uiState.currentInput =
603
- existing === '' ? transcript : `${existing} ${transcript}`;
604
- },
605
- prepare(transcript, chatSessionId) {
606
- return { payload: { chatSessionId, transcript } };
607
- },
608
- },
609
- transcribeAudioFailure(draft, action) {
610
- draft.transcriptionState.fetchState = 'Error';
611
- draft.transcriptionState.error = action.payload;
612
- },
613
554
  clearCurrentSessionId(draft) {
614
- resetFinishedTranscription(draft);
615
555
  // New Chat: the empty composer is the point, so the browser cannot stay over it.
616
556
  if (draft.uiState.isSkillsBrowserOpen === true) {
617
557
  closeSkillsBrowserState(draft);
@@ -895,8 +835,7 @@ const aiCfoView = createSlice({
895
835
  // The server has it. Nothing it sends from here on will contain this row,
896
836
  // so the tombstone has done its job and holding it any longer is what
897
837
  // makes a routine recreated under the same id invisible forever.
898
- draft.routines.deletedScheduleIds =
899
- draft.routines.deletedScheduleIds.filter((id) => id !== action.payload.scheduleId);
838
+ draft.routines.deletedScheduleIds = draft.routines.deletedScheduleIds.filter((id) => id !== action.payload.scheduleId);
900
839
  draft.routineMutation = {
901
840
  fetchState: 'Completed',
902
841
  error: undefined,
@@ -908,8 +847,7 @@ const aiCfoView = createSlice({
908
847
  // is what lets the refetch put the row back — leaving it hidden would
909
848
  // tell the user they had stopped something they had not, which is the
910
849
  // worst outcome this list can produce.
911
- draft.routines.deletedScheduleIds =
912
- draft.routines.deletedScheduleIds.filter((id) => id !== action.payload.scheduleId);
850
+ draft.routines.deletedScheduleIds = draft.routines.deletedScheduleIds.filter((id) => id !== action.payload.scheduleId);
913
851
  // Its own slice: the refetch that puts the row back would otherwise wipe
914
852
  // this error, leaving the row silently reappearing with no explanation.
915
853
  draft.routineMutation = {
@@ -1245,5 +1183,5 @@ const aiCfoView = createSlice({
1245
1183
  });
1246
1184
  },
1247
1185
  });
1248
- export const { acceptMasterTOS, acceptMasterTOSFailure, acceptMasterTOSSuccess, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, clearSkillDetail, clearSkillMutation, closeRoutineRuns, closeRoutinesBrowser, closeSkillsBrowser, createSession, createSessionAndSubmit, createSessionAndSubmitFailure, createSessionAndSubmitSuccess, createSessionFailure, createSessionSuccess, deleteChatSession, deleteChatSessionFailure, deleteChatSessionSuccess, deleteRoutine, deleteRoutineFailure, deleteRoutineSuccess, deleteSkill, deleteSkillFailure, deleteSkillSuccess, fetchChatHistory, fetchChatHistoryFailure, fetchChatHistorySuccess, fetchChatSessionsForUser, fetchChatSessionsForUserFailure, fetchChatSessionsForUserSuccess, fetchRoutineRuns, fetchRoutineRunsFailure, fetchRoutineRunsSuccess, fetchRoutines, fetchRoutinesFailure, fetchRoutinesSuccess, fetchSkillDetail, fetchSkillDetailFailure, fetchSkillDetailSuccess, fetchSkills, fetchSkillsFailure, fetchSkillsSuccess, fetchSkillVersionBody, fetchSkillVersionBodyFailure, fetchSkillVersionBodySuccess, fetchSkillVersions, fetchSkillVersionsFailure, fetchSkillVersionsSuccess, fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess, initializeNewSessionState, openRoutinesBrowser, openSkillsBrowser, runRoutine, runRoutineFailure, runRoutineSuccess, saveRoutine, saveRoutineFailure, saveRoutineSuccess, saveSkill, saveSkillFailure, saveSkillSuccess, setRoutineEnabled, setSession, setSkillVisibility, setSkillVisibilityFailure, setSkillVisibilitySuccess, stopSubmit, stopSubmitFailure, stopSubmitQuestion, stopSubmitQuestionFailure, stopSubmitQuestionSuccess, stopSubmitSuccess, submitFeedback, submitFeedbackFailure, submitFeedbackSuccess, submitQuestion, submitQuestionFailure, submitQuestionSuccess, transcribeAudio, transcribeAudioFailure, transcribeAudioSuccess, updateAiCfoViewScrollPosition, updateCotCollapsedState, updateCurrentInput, updateResponseState, } = aiCfoView.actions;
1186
+ export const { acceptMasterTOS, acceptMasterTOSFailure, acceptMasterTOSSuccess, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, clearSkillDetail, clearSkillMutation, closeRoutineRuns, closeRoutinesBrowser, closeSkillsBrowser, createSession, createSessionAndSubmit, createSessionAndSubmitFailure, createSessionAndSubmitSuccess, createSessionFailure, createSessionSuccess, deleteChatSession, deleteChatSessionFailure, deleteChatSessionSuccess, deleteRoutine, deleteRoutineFailure, deleteRoutineSuccess, deleteSkill, deleteSkillFailure, deleteSkillSuccess, fetchChatHistory, fetchChatHistoryFailure, fetchChatHistorySuccess, fetchChatSessionsForUser, fetchChatSessionsForUserFailure, fetchChatSessionsForUserSuccess, fetchRoutineRuns, fetchRoutineRunsFailure, fetchRoutineRunsSuccess, fetchRoutines, fetchRoutinesFailure, fetchRoutinesSuccess, fetchSkillDetail, fetchSkillDetailFailure, fetchSkillDetailSuccess, fetchSkills, fetchSkillsFailure, fetchSkillsSuccess, fetchSkillVersionBody, fetchSkillVersionBodyFailure, fetchSkillVersionBodySuccess, fetchSkillVersions, fetchSkillVersionsFailure, fetchSkillVersionsSuccess, fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess, initializeNewSessionState, openRoutinesBrowser, openSkillsBrowser, runRoutine, runRoutineFailure, runRoutineSuccess, saveRoutine, saveRoutineFailure, saveRoutineSuccess, saveSkill, saveSkillFailure, saveSkillSuccess, setRoutineEnabled, setSession, setSkillVisibility, setSkillVisibilityFailure, setSkillVisibilitySuccess, stopSubmit, stopSubmitFailure, stopSubmitQuestion, stopSubmitQuestionFailure, stopSubmitQuestionSuccess, stopSubmitSuccess, submitFeedback, submitFeedbackFailure, submitFeedbackSuccess, submitQuestion, submitQuestionFailure, submitQuestionSuccess, updateAiCfoViewScrollPosition, updateCotCollapsedState, updateCurrentInput, updateResponseState, } = aiCfoView.actions;
1249
1187
  export default aiCfoView.reducer;
@@ -133,28 +133,3 @@ export function getSkillMutation(state) {
133
133
  export function getSkillsEnabled(state) {
134
134
  return getSkills(state).skillsEnabled;
135
135
  }
136
- /** True while a finished voice recording is being transcribed server-side. */
137
- /**
138
- * A take belongs to the chat it was dictated into. A new chat has no session id yet on
139
- * either side, which is a match rather than a mismatch.
140
- */
141
- function isTranscribingCurrentSession(state) {
142
- if (state.aiCfoViewState == null) {
143
- return false;
144
- }
145
- return (state.aiCfoViewState.transcribingChatSessionId ===
146
- state.aiCfoViewState.uiState.currentChatSessionId);
147
- }
148
- export function getIsTranscribing(state) {
149
- return (state.aiCfoViewState?.transcriptionState?.fetchState === 'In-Progress' &&
150
- isTranscribingCurrentSession(state));
151
- }
152
- /**
153
- * Set when a transcription request failed. Without surfacing this the composer
154
- * would just drop the "Transcribing…" label and sit there silently, leaving the
155
- * user with no sign their recording went nowhere.
156
- */
157
- export function getTranscriptionFailed(state) {
158
- return (state.aiCfoViewState?.transcriptionState?.fetchState === 'Error' &&
159
- isTranscribingCurrentSession(state));
160
- }
@@ -88,6 +88,10 @@ export const fetchDashboardEpic = (actions$, state$) => actions$.pipe(filter(fet
88
88
  const revenueState = getRevenueReportFetchState(state.revenueState.uiState.selectedForecast?.id, state.revenueState);
89
89
  const opExState = getOperatingExpensesReportFetchState(state.opExState.uiState.selectedForecast?.id, state.opExState);
90
90
  const netBurnOrIncomeReportFetchState = getNetBurnOrIncomeReportFetchState(state.netBurnOrIncomeState.uiState.selectedForecast?.id, state.netBurnOrIncomeState);
91
+ if (state.tenantState.fetchState !== 'In-Progress' &&
92
+ !state.tenantState.hasValidState()) {
93
+ fetchActions.push(fetchAllTenants(userId));
94
+ }
91
95
  if (state.cardBalanceState.fetchState !== 'In-Progress' &&
92
96
  !state.cardBalanceState.hasValidState()) {
93
97
  fetchActions.push(fetchCardBalance());