mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
feat: register pages routes guards.
feat: retrieve all organizations details to authenticated user. feat: redux organization reducers and actions.
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "4.0.1",
|
||||
"react": "^16.12.0",
|
||||
"react-albus": "^2.0.0",
|
||||
"react-app-polyfill": "^1.0.6",
|
||||
"react-body-classname": "^1.3.1",
|
||||
"react-dev-utils": "^10.2.0",
|
||||
@@ -83,6 +84,7 @@
|
||||
"react-split-pane": "^0.1.91",
|
||||
"react-table": "^7.0.0",
|
||||
"react-table-sticky": "^1.1.2",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"react-use": "^13.26.1",
|
||||
"react-window": "^1.8.5",
|
||||
"redux": "^4.0.5",
|
||||
|
||||
@@ -20,7 +20,7 @@ function App({ locale }) {
|
||||
const queryConfig = {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
},
|
||||
};
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router';
|
||||
import classNames from 'classnames';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import DashboardLoadingIndicator from './DashboardLoadingIndicator';
|
||||
|
||||
import Sidebar from 'components/Sidebar/Sidebar';
|
||||
import DashboardContent from 'components/Dashboard/DashboardContent';
|
||||
@@ -9,29 +11,56 @@ import PreferencesContent from 'components/Preferences/PreferencesContent';
|
||||
import PreferencesSidebar from 'components/Preferences/PreferencesSidebar';
|
||||
import Search from 'containers/GeneralSearch/Search';
|
||||
import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane';
|
||||
import EnsureOrganizationIsReady from './EnsureOrganizationIsReady';
|
||||
|
||||
export default function Dashboard() {
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withOrganizationsActions from 'containers/Organization/withOrganizationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function Dashboard({
|
||||
// #withSettings
|
||||
requestFetchOptions,
|
||||
|
||||
// #withOrganizations
|
||||
requestOrganizationsList,
|
||||
}) {
|
||||
const fetchOrganizations = useQuery(
|
||||
['organizations'],
|
||||
(key) => requestOrganizationsList(),
|
||||
);
|
||||
return (
|
||||
<div className={classNames('dashboard')}>
|
||||
<Switch>
|
||||
<Route path="/preferences">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<PreferencesSidebar />
|
||||
</DashboardSplitPane>
|
||||
<PreferencesContent />
|
||||
</Route>
|
||||
<EnsureOrganizationIsReady>
|
||||
<DashboardLoadingIndicator
|
||||
isLoading={
|
||||
fetchOrganizations.isLoading
|
||||
}>
|
||||
<Switch>
|
||||
<Route path="/preferences">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<PreferencesSidebar />
|
||||
</DashboardSplitPane>
|
||||
<PreferencesContent />
|
||||
</Route>
|
||||
|
||||
<Route path="/">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<DashboardContent />
|
||||
</DashboardSplitPane>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Route path="/">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<DashboardContent />
|
||||
</DashboardSplitPane>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<Search />
|
||||
<DialogsContainer />
|
||||
</div>
|
||||
<Search />
|
||||
<DialogsContainer />
|
||||
</DashboardLoadingIndicator>
|
||||
</EnsureOrganizationIsReady>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettingsActions,
|
||||
withOrganizationsActions,
|
||||
)(Dashboard);
|
||||
25
client/src/components/Dashboard/DashboardLoadingIndicator.js
Normal file
25
client/src/components/Dashboard/DashboardLoadingIndicator.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Choose, Icon } from 'components';
|
||||
|
||||
export default function Dashboard({
|
||||
isLoading = false,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<div className={classNames('dashboard')}>
|
||||
<Choose>
|
||||
<Choose.When condition={isLoading}>
|
||||
<div class="center">
|
||||
<Icon icon="bigcapital" height={37} width={214} />
|
||||
<span>Please wait while resources loading...</span>
|
||||
</div>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
{ children }
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
16
client/src/components/Dashboard/EnsureOrganizationIsReady.js
Normal file
16
client/src/components/Dashboard/EnsureOrganizationIsReady.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
export default function EnsureOrganizationIsReady({
|
||||
children,
|
||||
}) {
|
||||
const isOrganizationReady = false;
|
||||
|
||||
return (isOrganizationReady) ? children : (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/register'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
import withOrganizationsActions from 'containers/Organization/withOrganizationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -29,6 +30,7 @@ const ERRORS_TYPES = {
|
||||
};
|
||||
function Login({
|
||||
requestLogin,
|
||||
requestOrganizationsList,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
@@ -168,4 +170,5 @@ function Login({
|
||||
|
||||
export default compose(
|
||||
withAuthenticationActions,
|
||||
withOrganizationsActions,
|
||||
)(Login);
|
||||
@@ -1,12 +1,23 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Icon } from 'components';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Icon, If } from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
export default function RegisterLeftSection({
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function RegisterLeftSection({
|
||||
requestLogout,
|
||||
isAuthorized
|
||||
}) {
|
||||
const [org] = useState('LibyanSpider');
|
||||
|
||||
const onClickLogout = useCallback(() => {
|
||||
requestLogout();
|
||||
}, [requestLogout]);
|
||||
|
||||
return (
|
||||
<section className={'register-page__left-section'}>
|
||||
<div className={'content'}>
|
||||
@@ -27,17 +38,18 @@ export default function RegisterLeftSection({
|
||||
<T id={'you_have_a_bigcapital_account'} />
|
||||
</p>
|
||||
|
||||
<div className={'content-org'}>
|
||||
<span>
|
||||
<T id={'welcome'} />
|
||||
{org},
|
||||
</span>
|
||||
<span>
|
||||
<a href={'#!'}>
|
||||
<T id={'sign_out'} />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<If condition={!!isAuthorized}>
|
||||
<div className={'content-org'}>
|
||||
<span>
|
||||
<T id={'welcome'} />
|
||||
{org},
|
||||
</span>
|
||||
<span>
|
||||
<a onClick={onClickLogout} href="#"><T id={'sign_out'} /></a>
|
||||
</span>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<div className={'content-contact'}>
|
||||
<a href={'#!'}>
|
||||
@@ -54,4 +66,9 @@ export default function RegisterLeftSection({
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(({ isAuthorized }) => ({ isAuthorized })),
|
||||
withAuthenticationActions,
|
||||
)(RegisterLeftSection);
|
||||
|
||||
@@ -1,26 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import { Wizard, Steps, Step } from 'react-albus';
|
||||
import { useHistory } from "react-router-dom";
|
||||
import RegisterWizardSteps from './RegisterWizardSteps';
|
||||
import registerRoutes from 'routes/register';
|
||||
import PrivateRoute from 'components/PrivateRoute';
|
||||
|
||||
import RegisterUserForm from 'containers/Authentication/Register/RegisterUserForm';
|
||||
import RegisterSubscriptionForm from 'containers/Authentication/Register/RegisterSubscriptionForm';
|
||||
import RegisterOrganizationForm from 'containers/Authentication/Register/RegisterOrganizationForm';
|
||||
|
||||
export default function RegisterRightSection () {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<section className={'register-page__right-section'}>
|
||||
<RegisterWizardSteps />
|
||||
<Wizard
|
||||
basename={'/register'}
|
||||
history={history}
|
||||
render={({ step, steps }) => (
|
||||
<div>
|
||||
<RegisterWizardSteps currentStep={steps.indexOf(step) + 1} />
|
||||
|
||||
<div class="register-page-form">
|
||||
<Switch>
|
||||
{ registerRoutes.map((route, index) => (
|
||||
<Route
|
||||
exact={route.exact}
|
||||
key={index}
|
||||
path={`${route.path}`}
|
||||
component={route.component}
|
||||
/>
|
||||
)) }
|
||||
</Switch>
|
||||
</div>
|
||||
<TransitionGroup>
|
||||
<CSSTransition
|
||||
key={step.id}
|
||||
classNames="example"
|
||||
timeout={{ enter: 500, exit: 500 }}
|
||||
>
|
||||
<div class="register-page-form">
|
||||
<Steps key={step.id} step={step}>
|
||||
<Step id="user">
|
||||
<RegisterUserForm />
|
||||
</Step>
|
||||
|
||||
<Step id="subscription">
|
||||
<PrivateRoute component={RegisterSubscriptionForm} />
|
||||
</Step>
|
||||
|
||||
<Step id="organization">
|
||||
<PrivateRoute component={RegisterOrganizationForm} />
|
||||
</Step>
|
||||
|
||||
<Step id="congratulations">
|
||||
<h1 className="text-align-center">Ice King</h1>
|
||||
</Step>
|
||||
</Steps>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
)} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -77,17 +77,12 @@ function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
requestRegister(values)
|
||||
.then((response) => {
|
||||
// AppToaster.show({
|
||||
// message: formatMessage({
|
||||
// id: 'welcome_organization_account_has_been_created',
|
||||
// }),
|
||||
// intent: Intent.SUCCESS,
|
||||
// });
|
||||
requestLogin({
|
||||
crediential: values.email,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
history.push('/register/subscription');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
@@ -98,7 +93,6 @@ function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
// history.push('/auth/login');
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
fetchOrganizations,
|
||||
} from 'store/organizations/organizations.actions';
|
||||
|
||||
export const mapDispatchToProps = (dispatch) => ({
|
||||
requestOrganizationsList: () => dispatch(fetchOrganizations()),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -0,0 +1,16 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getOrganizationByOrgIdFactory,
|
||||
} from 'store/organizations/organizations.selector';
|
||||
|
||||
export default (mapState) => {
|
||||
const getOrganizationByOrgId = getOrganizationByOrgIdFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
organization: getOrganizationByOrgId(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getOrganizationByTenantIdFactory,
|
||||
} from 'store/organizations/organizations.selector';
|
||||
|
||||
export default (mapState) => {
|
||||
const getOrgByTenId = getOrganizationByTenantIdFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
organization: getOrgByTenId(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ const initialState = {
|
||||
token: '',
|
||||
organization: '',
|
||||
user: '',
|
||||
tenant: {},
|
||||
locale: '',
|
||||
errors: [],
|
||||
};
|
||||
@@ -15,6 +16,7 @@ export default createReducer(initialState, {
|
||||
state.token = token;
|
||||
state.user = user;
|
||||
state.organization = tenant.organization_id;
|
||||
state.tenant = tenant;
|
||||
},
|
||||
|
||||
[t.LOGIN_FAILURE]: (state, action) => {
|
||||
@@ -36,3 +38,9 @@ export const isAuthenticated = (state) => !!state.authentication.token;
|
||||
export const hasErrorType = (state, errorType) => {
|
||||
return state.authentication.errors.find((e) => e.type === errorType);
|
||||
};
|
||||
|
||||
export const isTenantSeeded = (state) => !!state.tenant.seeded_at;
|
||||
export const isTenantBuilt = (state) => !!state.tenant.initialized_at;
|
||||
|
||||
export const isTenantHasSubscription = () => false;
|
||||
export const isTenantSubscriptionExpired = () => false;
|
||||
16
client/src/store/organizations/organizations.actions.js
Normal file
16
client/src/store/organizations/organizations.actions.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import ApiService from 'services/ApiService';
|
||||
import t from 'store/types';
|
||||
|
||||
export const fetchOrganizations = () => {
|
||||
return (dispatch) => new Promise((resolve, reject) => {
|
||||
ApiService.get('organization/all').then((response) => {
|
||||
dispatch({
|
||||
type: t.ORGANIZATIONS_LIST_SET,
|
||||
payload: {
|
||||
organizations: response.data.organizations,
|
||||
},
|
||||
});
|
||||
resolve(response)
|
||||
}).catch(error => { reject(error); });
|
||||
});
|
||||
};
|
||||
25
client/src/store/organizations/organizations.reducers.js
Normal file
25
client/src/store/organizations/organizations.reducers.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import t from 'store/types';
|
||||
|
||||
const initialState = {
|
||||
data: {},
|
||||
byOrganizationId: {},
|
||||
};
|
||||
|
||||
const reducer = createReducer(initialState, {
|
||||
|
||||
[t.ORGANIZATIONS_LIST_SET]: (state, action) => {
|
||||
const { organizations } = action.payload;
|
||||
const _data = {};
|
||||
const _dataByOrganizationId = {};
|
||||
|
||||
organizations.forEach((organization) => {
|
||||
_data[organization.id] = organization;
|
||||
_dataByOrganizationId[organization.organization_id] = organization.id;
|
||||
});
|
||||
state.data = _data;
|
||||
state.byOrganizationId = _dataByOrganizationId;
|
||||
},
|
||||
})
|
||||
|
||||
export default reducer;
|
||||
18
client/src/store/organizations/organizations.selectors.js
Normal file
18
client/src/store/organizations/organizations.selectors.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
const oragnizationByTenantIdSelector = (state, props) => state.organizations[props.tenantId];
|
||||
const organizationByIdSelector = (state, props) => state.organizations.byOrganizationId[props.organizationId];
|
||||
|
||||
export const getOrganizationByOrgIdFactory = () => createSelector(
|
||||
organizationByIdSelector,
|
||||
(organization) => {
|
||||
return organization;
|
||||
},
|
||||
);
|
||||
|
||||
export const getOrganizationByTenantIdFactory = () => createSelector(
|
||||
oragnizationByTenantIdSelector,
|
||||
(organization) => {
|
||||
return organization;
|
||||
}
|
||||
)
|
||||
5
client/src/store/organizations/organizations.types.js
Normal file
5
client/src/store/organizations/organizations.types.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
export default {
|
||||
ORGANIZATIONS_LIST_SET: 'ORGANIZATIONS_LIST_SET',
|
||||
};
|
||||
@@ -25,9 +25,11 @@ import bills from './Bills/bills.reducer';
|
||||
import vendors from './vendors/vendors.reducer';
|
||||
import paymentReceives from './PaymentReceive/paymentReceive.reducer';
|
||||
import paymentMades from './PaymentMades/paymentMade.reducer';
|
||||
import organizations from './organizations/organizations.reducers';
|
||||
|
||||
export default combineReducers({
|
||||
authentication,
|
||||
organizations,
|
||||
dashboard,
|
||||
users,
|
||||
accounts,
|
||||
|
||||
@@ -24,6 +24,7 @@ import bills from './Bills/bills.type';
|
||||
import vendors from './vendors/vendors.types';
|
||||
import paymentReceives from './PaymentReceive/paymentReceive.type';
|
||||
import paymentMades from './PaymentMades/paymentMade.type';
|
||||
import organizations from './organizations/organizations.types';
|
||||
|
||||
export default {
|
||||
...authentication,
|
||||
@@ -52,4 +53,5 @@ export default {
|
||||
...bills,
|
||||
...paymentReceives,
|
||||
...paymentMades,
|
||||
...organizations,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import asyncMiddleware from "api/middleware/asyncMiddleware";
|
||||
import JWTAuth from 'api/middleware/jwtAuth';
|
||||
import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
|
||||
@@ -27,12 +27,14 @@ export default class OrganizationController extends BaseController{
|
||||
router.use(JWTAuth);
|
||||
router.use(AttachCurrentTenantUser);
|
||||
router.use(TenancyMiddleware);
|
||||
router.use(SubscriptionMiddleware('main'));
|
||||
|
||||
|
||||
// Should to seed organization tenant be configured.
|
||||
router.use('/seed', SubscriptionMiddleware('main'));
|
||||
router.use('/seed', SettingsMiddleware);
|
||||
router.use('/seed', EnsureConfiguredMiddleware);
|
||||
|
||||
router.use('/build', SubscriptionMiddleware('main'));
|
||||
|
||||
router.post(
|
||||
'/build',
|
||||
asyncMiddleware(this.build.bind(this))
|
||||
@@ -41,6 +43,10 @@ export default class OrganizationController extends BaseController{
|
||||
'/seed',
|
||||
asyncMiddleware(this.seed.bind(this)),
|
||||
);
|
||||
router.get(
|
||||
'/all',
|
||||
asyncMiddleware(this.allOrganizations.bind(this)),
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -116,4 +122,21 @@ export default class OrganizationController extends BaseController{
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listing all organizations that assocaited to the authorized user.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async allOrganizations(req: Request, res: Response, next: NextFunction) {
|
||||
const { user } = req;
|
||||
|
||||
try {
|
||||
const organizations = await this.organizationService.listOrganizations(user);
|
||||
return res.status(200).send({ organizations });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { ITenant } from 'interfaces';
|
||||
import { ISystemService, ISystemUser, ITenant } from 'interfaces';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -38,7 +38,7 @@ export default class OrganizationService {
|
||||
* @param {srting} organizationId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async build(organizationId: string): Promise<void> {
|
||||
public async build(organizationId: string): Promise<void> {
|
||||
const tenant = await this.getTenantByOrgIdOrThrowError(organizationId);
|
||||
this.throwIfTenantInitizalized(tenant);
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class OrganizationService {
|
||||
* @param {number} organizationId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async seed(organizationId: string): Promise<void> {
|
||||
public async seed(organizationId: string): Promise<void> {
|
||||
const tenant = await this.getTenantByOrgIdOrThrowError(organizationId);
|
||||
this.throwIfTenantSeeded(tenant);
|
||||
|
||||
@@ -91,6 +91,20 @@ export default class OrganizationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listing all associated organizations to the given user.
|
||||
* @param {ISystemUser} user -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async listOrganizations(user: ISystemUser): Promise<ITenant[]> {
|
||||
this.logger.info('[organization] trying to list all organizations.', { user });
|
||||
|
||||
const { tenantRepository } = this.sysRepositories;
|
||||
const tenant = await tenantRepository.getByIdWithSubscriptions(user.tenantId);
|
||||
|
||||
return [tenant];
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws error in case the given tenant is undefined.
|
||||
* @param {ITenant} tenant
|
||||
|
||||
@@ -63,11 +63,23 @@ export default class TenantRepository extends SystemRepository {
|
||||
|
||||
/**
|
||||
* Retrieve tenant details by the given tenant id.
|
||||
* @param {string} tenantId
|
||||
* @param {string} tenantId - Tenant id.
|
||||
*/
|
||||
getById(tenantId: number) {
|
||||
return this.cache.get(`tenant.id.${tenantId}`, () => {
|
||||
return Tenant.query().findById(tenantId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve tenant details with associated subscriptions
|
||||
* and plans by the given tenant id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
*/
|
||||
getByIdWithSubscriptions(tenantId: number) {
|
||||
return this.cache.get(`tenant.id.${tenantId}.subscriptions`, () => {
|
||||
return Tenant.query().findById(tenantId)
|
||||
.withGraphFetched('subscriptions.plan');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user