@zeniai/client-epic-state 5.3.16 → 5.3.18-betaAK1

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.
@@ -1,62 +1,65 @@
1
- import { defer, from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
1
+ import { defer, from, merge, of } from 'rxjs';
2
+ import { catchError, filter, map, mergeMap, switchMap, take, } from 'rxjs/operators';
3
3
  import { toMonth } from '../../../commonStateTypes/timePeriod';
4
- import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
4
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
5
5
  import { getSessionEndpointsLoader } from '../../../sessionEndpointsLoader';
6
- import { initializeSelectedPeriodForTenants } from '../../../view/expenseAutomationView/expenseAutomationViewReducer';
7
6
  import { date, dateNow } from '../../../zeniDayJS';
8
- import { updateCompanies } from '../../company/companyReducer';
9
- import { fetchAllTenants, updateTenantsFailure, updateTenantsSuccess, } from '../tenantReducer';
7
+ import { fetchActiveTenant, fetchAllTenants, updateTenantFailure, updateTenantSuccess, updateTenantSummariesSuccess, updateTenantsFailure, } from '../tenantReducer';
8
+ // Only what the tenant switcher and tenant lookups read (~0.3 KB a tenant
9
+ // instead of ~18 KB with full records and companies). A tenant's full record
10
+ // comes from `fetchActiveTenant` when it is opened.
11
+ const TENANT_SUMMARIES_QUERY = {
12
+ is_full: false,
13
+ is_include_companies_in_response: false,
14
+ };
15
+ /**
16
+ * The tenant the app lands on: the selected one, or the most recently opened
17
+ * when none is selected (sign-in, the root route).
18
+ */
19
+ function getLandingTenantId(tenants, currentTenantId) {
20
+ if (currentTenantId != null &&
21
+ tenants.some((tenant) => tenant.tenant_id === currentTenantId)) {
22
+ return currentTenantId;
23
+ }
24
+ const lastCheckIn = (tenant) => tenant.user_last_check_in_time_into_tenant != null
25
+ ? date(tenant.user_last_check_in_time_into_tenant).valueOf()
26
+ : 0;
27
+ return tenants.reduce((mostRecent, tenant) => mostRecent == null || lastCheckIn(tenant) > lastCheckIn(mostRecent)
28
+ ? tenant
29
+ : mostRecent, undefined)?.tenant_id;
30
+ }
10
31
  export const fetchAllTenantsEpic = (actions$, state, zeniAPI) => {
11
- const queryParam = {
12
- is_include_companies_in_response: true,
13
- };
14
32
  return actions$.pipe(filter(fetchAllTenants.match), switchMap((action) => {
15
- const fetchTenants$ = defer(() => zeniAPI.getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/users/${action.payload.userId}/tenants?query=${encodeURIComponent(JSON.stringify(queryParam))}`, Object.assign({ 'zeni-user-id': action.payload.userId }, { 'zeni-tenant-id': '' }, action.payload.zeniSessionId != null
16
- ? { 'zeni-session-id': action.payload.zeniSessionId }
17
- : {}))).pipe(mergeMap((response) => {
18
- const currentTenantId = state.value.tenantState.currentTenantId;
19
- if (isSuccessResponse(response) && response.data != null) {
20
- const defaultSelectedPeriodsForTenants = getDefaultSelectedPeriodsForTenants(response.data);
21
- let actions = [
22
- updateTenantsSuccess(response.data),
23
- initializeSelectedPeriodForTenants({
24
- selectedPeriodByTenantId: defaultSelectedPeriodsForTenants,
25
- }),
26
- ];
27
- if (response.data.companies != null) {
28
- let companiesPayload = response.data.companies;
29
- if (currentTenantId != null) {
30
- // filter out this tenant ID related company detail
31
- // Find companyID for this tenant
32
- const currentTenantPayload = response.data.tenants.find((tenant) => tenant.tenant_id === currentTenantId);
33
- if (currentTenantPayload != null) {
34
- companiesPayload = response.data.companies.filter((company) => company.company_id !== currentTenantPayload.company_id);
35
- }
36
- else {
37
- console.warn('Selected tenant record not found in response', currentTenantId);
38
- }
39
- }
40
- actions = [
41
- updateCompanies({
42
- payload: companiesPayload,
43
- schema: {},
44
- }),
45
- ...actions,
46
- ];
47
- }
48
- return from(actions);
49
- }
50
- else {
33
+ const { userId, zeniSessionId } = action.payload;
34
+ const fetchTenants$ = defer(() => zeniAPI.getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/users/${userId}/tenants?query=${encodeURIComponent(JSON.stringify(TENANT_SUMMARIES_QUERY))}`, Object.assign({ 'zeni-user-id': userId }, { 'zeni-tenant-id': '' }, zeniSessionId != null ? { 'zeni-session-id': zeniSessionId } : {}))).pipe(mergeMap((response) => {
35
+ if (!isSuccessResponse(response) || response.data == null) {
51
36
  return of(updateTenantsFailure(response.status));
52
37
  }
38
+ const { tenants } = response.data;
39
+ if (tenants.length === 0) {
40
+ return of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Empty list of tenants')));
41
+ }
42
+ const tenantsLoaded = updateTenantSummariesSuccess(tenants);
43
+ const { tenantState } = state.value;
44
+ const landingTenantId = getLandingTenantId(tenants, tenantState.currentTenantId);
45
+ const isLandingTenantLoadedOrLoading = landingTenantId == null ||
46
+ tenantState.tenantsById[landingTenantId] != null ||
47
+ tenantState.activeTenantFetchState.fetchState === 'In-Progress';
48
+ if (isLandingTenantLoadedOrLoading) {
49
+ return of(tenantsLoaded);
50
+ }
51
+ // Screens that wait for this fetch (sign-in, the root route) read the
52
+ // landing tenant's roles and product settings, which the list does
53
+ // not carry, so it completes only once that tenant's record is in.
54
+ return merge(actions$.pipe(filter((landingAction) => updateTenantSuccess.match(landingAction) ||
55
+ updateTenantFailure.match(landingAction)), take(1), map(() => tenantsLoaded)), of(fetchActiveTenant(userId, landingTenantId, zeniSessionId)));
53
56
  }), catchError((error) => of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Fetch All Tenants REST API call errored out' +
54
57
  JSON.stringify(error))))));
55
58
  const sessionEndpointsLoader = getSessionEndpointsLoader();
56
59
  if (sessionEndpointsLoader == null) {
57
60
  return fetchTenants$;
58
61
  }
59
- return defer(() => from(sessionEndpointsLoader(action.payload.userId, action.payload.zeniSessionId))).pipe(switchMap(() => fetchTenants$), catchError((error) => of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Loading session endpoints errored out: ' +
62
+ return defer(() => from(sessionEndpointsLoader(userId, zeniSessionId))).pipe(switchMap(() => fetchTenants$), catchError((error) => of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Loading session endpoints errored out: ' +
60
63
  JSON.stringify(error))))));
61
64
  }));
62
65
  };
@@ -17,6 +17,7 @@ export const initialState = {
17
17
  error: undefined,
18
18
  accountLocked: false,
19
19
  tenantsById: {},
20
+ tenantSummariesById: {},
20
21
  onboardingTenantsById: {},
21
22
  tenantIds: [],
22
23
  signInState: 'Not-Started',
@@ -107,26 +108,24 @@ const tenant = createSlice({
107
108
  });
108
109
  },
109
110
  },
110
- updateTenantsSuccess(draft, action) {
111
- const { tenants, user } = action.payload;
111
+ updateTenantSummariesSuccess(draft, action) {
112
112
  draft.fetchState = 'Completed';
113
113
  draft.error = undefined;
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;
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;
125
126
  }
126
127
  });
127
- draft.tenantsById = tenantsbyId;
128
- draft.tenantIds = tenantIds;
129
- draft.loggedInUser = assignWith(draft.loggedInUser ?? {}, firstUser, (objValue, srcValue) => srcValue ?? objValue);
128
+ draft.tenantsById = accessibleTenantsById;
130
129
  },
131
130
  updateTenantsFailure(draft, action) {
132
131
  draft.fetchState = 'Error';
@@ -134,6 +133,7 @@ const tenant = createSlice({
134
133
  draft.error = action.payload;
135
134
  draft.tenantIds = [];
136
135
  draft.tenantsById = {};
136
+ draft.tenantSummariesById = {};
137
137
  },
138
138
  updateTenantSuccess(draft, action) {
139
139
  const { tenants, user } = action.payload;
@@ -242,6 +242,7 @@ const tenant = createSlice({
242
242
  draft.signInState = 'Error';
243
243
  draft.tenantIds = [];
244
244
  draft.tenantsById = {};
245
+ draft.tenantSummariesById = {};
245
246
  },
246
247
  updateTenantReimbursementInfo(draft, action) {
247
248
  const { tenant_id, reimbursement_info } = action.payload;
@@ -934,7 +935,7 @@ const tenant = createSlice({
934
935
  });
935
936
  },
936
937
  });
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;
938
+ export const { updateTenants, fetchAllTenants, updateTenantSummariesSuccess, 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;
938
939
  export default tenant.reducer;
939
940
  /**
940
941
  * Applies treasury promo user fields from the tenants API user block when present.
@@ -976,6 +977,16 @@ function mapTenantsPayloadToState(tenantsPayload, userTenantPayload) {
976
977
  const firstUser = mapTenantPayloadToUser(tenantsPayload[0]);
977
978
  return [tenantsById, tenantIds, firstUser];
978
979
  }
980
+ function mapTenantSummaryPayloadToTenantSummary(payload) {
981
+ return {
982
+ companyId: payload.company_id,
983
+ companyName: payload.company_name,
984
+ tenantEmailDomain: payload.tenant_email_domain,
985
+ tenantId: payload.tenant_id,
986
+ // A tenant never opened sorts last, as in `mapTenantPayloadToTenant`.
987
+ userLastCheckInTime: date(payload.user_last_check_in_time_into_tenant ?? '1970-01-01'),
988
+ };
989
+ }
979
990
  function mapTenantPayloadToTenant(payload, userTenantPayload) {
980
991
  const tenant = {
981
992
  accountingClassIDs: payload.accounting_class_ids,
@@ -16,50 +16,75 @@ 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)?.tenantId;
19
+ return (Object.values(tenantState.tenantsById).find((tenant) => tenant.companyId === companyId) ??
20
+ Object.values(tenantState.tenantSummariesById).find((tenantSummary) => tenantSummary.companyId === companyId))?.tenantId;
20
21
  }
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
- };
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');
30
47
  }
31
- export const getTenantBaseViewForTenantId = createSelector((state) => state.tenantState, (state) => state.companyState, (_state, tenantId) => tenantId, (tenantState, companyState, tenantId) => {
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
- };
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
+ }
49
62
  }
50
- return undefined;
63
+ const mostRecentLoadedTenant = getTenantListEntriesByCheckInDate(tenantState).find((entry) => tenantState.tenantsById[entry.tenantId] != null);
64
+ return mostRecentLoadedTenant != null
65
+ ? getTenant(tenantState, companyState, mostRecentLoadedTenant.tenantId)
66
+ : undefined;
67
+ }
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
+ 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;
51
84
  });
52
85
  const getCurrentTenantHelper = (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
- }
86
+ const { currentTenantId, excludedResourcesByTenant, subscriptionSummaryByTenant, } = tenantState;
87
+ const tenant = getCurrentTenantView(tenantState, companyState);
63
88
  let excludedResources = undefined;
64
89
  if (tenant != null) {
65
90
  excludedResources = excludedResourcesByTenant[tenant.tenantId];
@@ -120,9 +145,12 @@ const getCurrentTenantHelper = (tenantState, companyState) => {
120
145
  export const getCurrentTenant = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
121
146
  return getCurrentTenantHelper(tenantState, companyState);
122
147
  });
123
- export const getTenantsByCheckInDateSelector = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
124
- return getTenantsByCheckInDate(tenantState, companyState);
125
- });
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;
126
154
  export function getOnboardingTenantByTenantId(tenantState, tenantId) {
127
155
  const tenant = tenantState.onboardingTenantsById[tenantId];
128
156
  if (tenant != null) {
@@ -133,10 +161,7 @@ export function getOnboardingTenantByTenantId(tenantState, tenantId) {
133
161
  }
134
162
  }
135
163
  export const getTenantBaseById = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
136
- return tenantState.tenantIds.map((tenantId) => {
137
- const tenant = tenantState.tenantsById[tenantId];
138
- return getTenantBaseViewForTenant(tenant, companyState);
139
- });
164
+ return getTenantListEntriesByCheckInDate(tenantState).map((entry) => getTenantBaseViewForEntry(entry, companyState));
140
165
  });
141
166
  export const getTenantsBaseByCheckInDateSelector = createSelector((state) => state.tenantState, (state) => state.companyState, (tenantState, companyState) => {
142
167
  return getTenantsBaseByCheckInDate(tenantState, companyState);
@@ -165,16 +190,8 @@ export const isTenantUsingZeniCOA = (currentTenant) => {
165
190
  return currentTenant.company?.companyInfo.isZeniCOAInUse ?? true; // defaulting to true
166
191
  };
167
192
  export const getCurrentTenantExternalConnections = (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
- }
193
+ const { externalConnectionsByTenant, currentTenantId } = tenantState;
194
+ const tenant = getCurrentTenantView(tenantState, companyState);
178
195
  let externalConnections = undefined;
179
196
  if (tenant != null) {
180
197
  externalConnections = externalConnectionsByTenant[tenant.tenantId];
@@ -188,58 +205,39 @@ export const getCurrentTenantExternalConnections = (tenantState, companyState) =
188
205
  * Helpers
189
206
  */
190
207
  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));
196
208
  return {
197
209
  order: 'descending_last_check_in',
198
- tenantsBaseInOrder: tenantsBaseOrdered,
210
+ tenantsBaseInOrder: getTenantListEntriesByCheckInDate(tenantState).map((entry) => getTenantBaseViewForEntry(entry, companyState)),
199
211
  };
200
212
  }
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;
213
+ const getTenantBaseViewForEntry = (entry, companyState) => {
214
+ const company = companyState.companiesById[entry.companyId];
209
215
  return {
210
- tenantId,
211
- tenantName,
212
- tenantEmailDomain,
213
- companyId,
214
- companyName,
215
- userRole,
216
- companyLocaleInfo,
216
+ tenantId: entry.tenantId,
217
+ tenantName: entry.companyName ??
218
+ company?.companyInfo.name ??
219
+ entry.tenantEmailDomain ??
220
+ '',
221
+ tenantEmailDomain: entry.tenantEmailDomain,
222
+ companyId: entry.companyId,
223
+ // Companies are loaded only for opened tenants; the list carries the name.
224
+ companyName: company?.companyInfo.name ?? entry.companyName,
225
+ userRole: entry.userRole,
226
+ companyLocaleInfo: company?.companyLocaleInfo,
217
227
  };
218
228
  };
219
229
  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));
225
230
  return {
226
231
  order: 'descending_last_check_in',
227
- tenantsCoreDetailsInOrder: tenantsCoreDetailsOrdered,
232
+ tenantsCoreDetailsInOrder: getTenantListEntriesByCheckInDate(tenantState).map((entry) => ({
233
+ tenantId: entry.tenantId,
234
+ tenantName: entry.companyName ??
235
+ companyState.companiesById[entry.companyId]?.companyInfo.name,
236
+ tenantEmailDomain: entry.tenantEmailDomain,
237
+ companyId: entry.companyId,
238
+ })),
228
239
  };
229
240
  }
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
- };
243
241
  export const toTenantCoreDetailsView = (tenantsBaseView) => {
244
242
  return tenantsBaseView.map(({ tenantId, tenantName, tenantEmailDomain, companyId }) => {
245
243
  return {
package/lib/esm/index.js CHANGED
@@ -73,7 +73,7 @@ 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
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';
76
+ import { DEFAULT_RECEIPTS_EMAIL, getCurrentTenant, getCurrentTenantId, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isCurrentTenantLoaded, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantLoaded, 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';
@@ -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, 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, };
512
+ export { getTenantBaseById, getCurrentTenant, getCurrentTenantId, isCurrentTenantLoaded, isTenantLoaded, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, 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 };
@@ -88,10 +88,6 @@ 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
- }
95
91
  if (state.cardBalanceState.fetchState !== 'In-Progress' &&
96
92
  !state.cardBalanceState.hasValidState()) {
97
93
  fetchActions.push(fetchCardBalance());
@@ -1,7 +1,7 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
2
  import { newBalancesFilter } from '../../commonStateTypes/coaBalance/coaBalancesFilter';
3
3
  import { reduceFetchState } from '../../commonStateTypes/reduceFetchState';
4
- import { getCurrentTenant, getTenantsByCheckInDateSelector, } from '../../entity/tenant/tenantSelector';
4
+ import { getCurrentTenant } from '../../entity/tenant/tenantSelector';
5
5
  import { hasAdminLevelAccess, hasInvestorBankerLevelAccess, } from '../../entity/userRole/userRoleType';
6
6
  import { getApAgingReport } from '../apAgingView/apAgingReport/apAgingSelector';
7
7
  import { getArAgingReport } from '../arAgingView/arAgingReport/arAgingSelector';
@@ -34,8 +34,7 @@ const getCashInCashOutLast4Periods = (state) => getCashInCashOut(state.accountSt
34
34
  const getNetBurnOrIncomeStoryCard = (state) => getNetBurnOrIncomeStoryCardReport(state);
35
35
  const apAgingSelector = (state) => getApAgingReport(state);
36
36
  const arAgingSelector = (state) => getArAgingReport(state);
37
- const tenantSelector = createSelector(getTenantsByCheckInDateSelector, getCurrentTenant, (tenantsOrdered, currentTenant) => ({
38
- tenantsOrdered,
37
+ const tenantSelector = createSelector(getCurrentTenant, (currentTenant) => ({
39
38
  currentTenant,
40
39
  }));
41
40
  export const getDashboard = createSelector(tenantSelector, getCardBalance, getCashBalance, getOperatingExpensesForLast5Periods, getRevenueForLast4Periods, getTop6Expenses, getNetBurnOrIncomeForLast4Periods, getNetBurnOrIncomeStoryCard, getCashPositionLast5Periods, getCashInCashOutLast4Periods, getInsights, getCompanyPassportView, getTasksCardReport, getBillPayCardReport, getReimbursementCardReport, apAgingSelector, arAgingSelector, getRewardsPlanCard, getDashboardCardsOrdered, (state) => {
@@ -15,12 +15,8 @@ export const fetchFinanceStatementEpic = (actions$, state$) => actions$.pipe(fil
15
15
  return of(fetchAllTenants(userId), fetchProfitAndLoss(), fetchBalanceSheet(), fetchCashFlow());
16
16
  }
17
17
  else {
18
- const { tenantState, financeStatementState } = state;
18
+ const { financeStatementState } = state;
19
19
  const fetchActions = [];
20
- if (tenantState.fetchState !== 'In-Progress' &&
21
- !tenantState.hasValidState()) {
22
- fetchActions.push(fetchAllTenants(userId));
23
- }
24
20
  const pandLFetchState = getPandLReportFetchState(undefined, state.profitAndLossState);
25
21
  const balanceSheetFetchState = getBalanceSheetFetchState(state.balanceSheetState);
26
22
  const cashFlowFetchState = getCashFlowReportFetchState(state.cashFlowState);
@@ -10,10 +10,6 @@ export const fetchPeoplePageEpic = (actions$, state$) => actions$.pipe(filter(fe
10
10
  else {
11
11
  const state = state$.value;
12
12
  const fetchActions = [];
13
- if (state.tenantState.fetchState !== 'In-Progress' &&
14
- !state.tenantState.hasValidState()) {
15
- fetchActions.push(fetchAllTenants(userId));
16
- }
17
13
  if (state.peopleState.fetchState != 'In-Progress' &&
18
14
  !state.peopleState.hasValidState()) {
19
15
  fetchActions.push(fetchPeople());
package/lib/index.d.ts CHANGED
@@ -147,7 +147,7 @@ import { getTaskGroupById } from './entity/taskGroup/taskGroupSelector';
147
147
  import { TaskGroupState } from './entity/taskGroup/taskGroupState';
148
148
  import { TaskGroupTemplate } from './entity/taskGroupTemplate/taskGroupTemplateState';
149
149
  import { DoSignInPayload, 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';
150
- import { CurrentTenant, DEFAULT_RECEIPTS_EMAIL, TenantBaseView, TenantCoreDetailsView, TenantView, TenantsBaseOrdered, TenantsOrdered, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView } from './entity/tenant/tenantSelector';
150
+ import { CurrentTenant, DEFAULT_RECEIPTS_EMAIL, TenantBaseView, TenantCoreDetailsView, TenantView, TenantsBaseOrdered, getCurrentTenant, getCurrentTenantId, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isCurrentTenantLoaded, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantLoaded, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView } from './entity/tenant/tenantSelector';
151
151
  import { CapitalizableAccount, CapitalizableAccounts, CapitalizableClassificationType, CapitalizableMatchPattern, Connection, LoggedInUser, RoleResource, Tenant, TenantProductSettings } from './entity/tenant/tenantState';
152
152
  import { toExternalSupportedTool } from './entity/tenant/tenantTypes';
153
153
  import { ToastNotificationPayload } from './entity/toastNotification/toastNotificationPayload';
@@ -754,7 +754,7 @@ export { TimeframeTick, TimeframeTickWithMetaData, toTimeframeTick, mapTimePerio
754
754
  export { VendorSpendTrendFilterTabType, toVendorSpendTrendFilterTabsTypeStrict, } from './entity/vendorExpense/vendorSpendTrendFilterTabType';
755
755
  export { getNumberOfPeriods };
756
756
  export { SCHEDULE_DAYS_OF_MONTH, ScheduleDaysOfMonth, toScheduleDaysOfMonth, Day, Month, toMonth, toMonthStrict, Quarter, toQuarter, toQuarterStrict, AbsoluteDay, TimePeriod, };
757
- export { CapitalizableAccount, CapitalizableAccounts, CapitalizableClassificationType, CapitalizableMatchPattern, Tenant, TenantView, LoggedInUser, TenantBaseView, CurrentTenant, TenantProductSettings, getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, TenantsBaseOrdered, TenantsOrdered, TenantCoreDetailsView, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, DoSignInPayload, doSignOut, sendSessionHeartbeat, resetSignInState, RoleResource, Connection, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
757
+ export { CapitalizableAccount, CapitalizableAccounts, CapitalizableClassificationType, CapitalizableMatchPattern, Tenant, TenantView, LoggedInUser, TenantBaseView, CurrentTenant, TenantProductSettings, getTenantBaseById, getCurrentTenant, getCurrentTenantId, isCurrentTenantLoaded, isTenantLoaded, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getIsAutoReviewIncomeWithoutCustomerEnabled, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, TenantsBaseOrdered, TenantCoreDetailsView, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, DoSignInPayload, doSignOut, sendSessionHeartbeat, resetSignInState, RoleResource, Connection, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
758
758
  export { Account, AccountType, AccountBase, StatementCloseDay, toAccountType, AccountReport, NestedAccountReport, AccountWithBalanceReport, AccountGroup, AccountGroupReportV2, AccountGroupType, toAccountGroupType, AccountGroupReport, getAccountGroupKey, AccountGroupKey, RecommendedAccountBase, AccountFilterOption, getAllAccounts, getTransactionFilterAccountOptions, };
759
759
  export { Class, ClassBase, RecommendedClassBase, } from './entity/class/classState';
760
760
  export { ClassFilterOption, getAllClasses, getClassById, getClassFilterOptions, } from './entity/class/classSelector';