chrone: sperate client and server to different repos.

This commit is contained in:
a.bouhuolia
2021-09-21 17:13:53 +02:00
parent e011b2a82b
commit 18df5530c7
10015 changed files with 17686 additions and 97524 deletions

View File

@@ -0,0 +1,15 @@
import t from 'store/types';
export const setBillsTableState = (queries) => {
return {
type: t.BILLS_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetBillsTableState = () => {
return {
type: t.BILLS_TABLE_STATE_RESET,
};
};

View File

@@ -0,0 +1,34 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:bills';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('BILLS', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,25 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './bills.reducer';
const billsTableStateSelector = (state) => state.bills.tableState;
// Get bills table state marged with location query.
export const getBillsTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
billsTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const billsTableStateChangedFactory = () =>
createDeepEqualSelector(billsTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,5 @@
export default {
BILLS_TABLE_STATE_SET: 'BILLS/TABLE_STATE_SET',
BILLS_TABLE_STATE_RESET: 'BILLS/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,14 @@
import t from 'store/types';
export const setEstimatesTableState = (queries) => {
return {
type: t.ESTIMATES_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetEstimatesTableState = () => {
return {
type: t.ESTIMATES_TABLE_STATE_RESET,
};
}

View File

@@ -0,0 +1,39 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import {
createTableStateReducers,
} from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:estimates';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('ESTIMATES', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(
CONFIG,
reducerInstance,
);

View File

@@ -0,0 +1,24 @@
import { isEqual } from 'lodash';
import { createDeepEqualSelector } from 'utils';
import { paginationLocationQuery } from 'store/selectors';
import { defaultTableQuery } from './estimates.reducer';
const estimatesTableState = (state) => state.salesEstimates.tableState;
// Retrieve estimates table query.
export const getEstimatesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
estimatesTableState,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const isEstimatesTableStateChangedFactory = () =>
createDeepEqualSelector(estimatesTableState, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,4 @@
export default {
ESTIMATES_TABLE_STATE_SET: 'ESTIMATES/TABLE_STATE_SET',
ESTIMATES_TABLE_STATE_RESET: 'ESTIMATES/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,8 @@
import t from 'store/types';
export const setExchangeRateTableState = (queries) => {
return {
type: t.EXCHANGE_RATES_TABLE_STATE_SET,
payload: { queries },
};
};

View File

@@ -0,0 +1,13 @@
import { createReducer } from '@reduxjs/toolkit';
import { createTableStateReducers } from 'store/tableState.reducer';
const initialState = {
tableState: {
pageSize: 12,
pageIndex: 0,
},
};
export default createReducer(initialState, {
...createTableStateReducers('EXCHANGE_RATES'),
});

View File

@@ -0,0 +1,18 @@
import { createDeepEqualSelector } from 'utils';
import { paginationLocationQuery } from 'store/selectors';
const exchangeRateTableState = (state) => {
return state.exchangeRates.tableState;
};
export const getExchangeRatesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
exchangeRateTableState,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);

View File

@@ -0,0 +1,3 @@
export default {
EXCHANGE_RATES_TABLE_STATE_SET: 'EXCHANGE_RATES/TABLE_STATE_SET',
};

View File

@@ -0,0 +1,16 @@
import t from 'store/types';
export const setInvoicesTableState = (queries) => {
return {
type: t.INVOICES_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetInvoicesTableState= () => {
return {
type: t.INVOICES_TABLE_STATE_RESET,
};
}
export const setSelectedRowsItems = () => {};

View File

@@ -0,0 +1,39 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import {
createTableStateReducers,
} from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:invoices';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('INVOICES', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
}
});
export default persistReducer(
CONFIG,
reducerInstance,
);

View File

@@ -0,0 +1,32 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './invoices.reducer';
const invoicesTableStateSelector = (state) => state.salesInvoices.tableState;
/**
* Retrieve invoices table state.
*/
export const getInvoicesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
invoicesTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
/**
* Retrieve invoices table state.
*/
export const isInvoicesTableStateChangedFactory = () =>
createDeepEqualSelector(
invoicesTableStateSelector,
(tableState) => {
return !isEqual(tableState, defaultTableQuery);
},
);

View File

@@ -0,0 +1,5 @@
export default {
INVOICES_TABLE_STATE_SET: 'INVOICES/TABLE_STATE_SET',
INVOICES_TABLE_STATE_RESET: 'INVOICES/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,16 @@
import t from 'store/types';
export const setPaymentMadesTableState = (queries) => {
return {
type: t.PAYMENT_MADES_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetPaymentMadesTableState = (queries) => {
return {
type: t.PAYMENT_MADES_TABLE_STATE_RESET,
payload: { queries },
};
};

View File

@@ -0,0 +1,35 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
sortBy: [],
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:paymentMades';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('PAYMENT_MADES', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,25 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './paymentMades.reducer';
const paymentMadesTableStateSelector = (state) => state.paymentMades.tableState;
// Get payment mades table state marged with location query.
export const getPaymentMadesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
paymentMadesTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const paymentsTableStateChangedFactory = () =>
createDeepEqualSelector(paymentMadesTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,4 @@
export default {
PAYMENT_MADES_TABLE_STATE_SET: 'PAYMENT_MADES/TABLE_STATE_SET',
PAYMENT_MADES_TABLE_STATE_RESET: 'PAYMENT_MADES/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,14 @@
import t from 'store/types';
export const setPaymentReceivesTableState = (queries) => {
return {
type: t.PAYMENT_RECEIVES_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetPaymentReceivesTableState = () => {
return {
type: t.PAYMENT_RECEIVES_TABLE_STATE_RESET
};
}

View File

@@ -0,0 +1,39 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import {
createTableStateReducers,
} from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:paymentReceives';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('PAYMENT_RECEIVES', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(
CONFIG,
reducerInstance,
);

View File

@@ -0,0 +1,26 @@
import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import {
paginationLocationQuery,
} from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './paymentReceives.reducer';
const paymentReceiveTableState = (state) => state.paymentReceives.tableState;
// Retrieve payment receives table fetch query.
export const getPaymentReceiveTableStateFactory = () => createSelector(
paginationLocationQuery,
paymentReceiveTableState,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const paymentsTableStateChangedFactory = () =>
createDeepEqualSelector(paymentReceiveTableState, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,4 @@
export default {
PAYMENT_RECEIVES_TABLE_STATE_SET: 'PAYMENT_RECEIVES/TABLE_STATE_SET',
PAYMENT_RECEIVES_TABLE_STATE_RESET: 'PAYMENT_RECEIVES/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,13 @@
export default (next) => (reducer, initialState, enhancer) => {
let resetType = 'RESET'
let resetData = 'state'
const enhanceReducer = (state, action) => {
if (action.type === resetType) {
state = action[resetData]
}
return reducer(state, action)
}
return next(enhanceReducer, initialState, enhancer)
}

View File

@@ -0,0 +1,17 @@
import t from 'store/types';
export const setAccountsTableState = (queries) => {
return {
type: t.ACCOUNTS_TABLE_STATE_SET,
payload: { queries },
};
};
/**
* Resets the accounts table state.
*/
export const resetAccountsTableState = () => {
return {
type: t.ACCOUNTS_TABLE_STATE_RESET,
};
};

View File

@@ -0,0 +1,34 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
};
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:accounts';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('ACCOUNTS', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
}
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,26 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './accounts.reducer';
// Accounts table state selector
const accountsTableStateSelector = (state, props) => state.accounts.tableState;
// Get accounts table state marged with location query.
export const getAccountsTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
accountsTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const accountsTableStateChangedFactory = () =>
createDeepEqualSelector(accountsTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,5 @@
export default {
ACCOUNTS_TABLE_STATE_SET: 'ACCOUNTS/TABLE_STATE_SET',
ACCOUNTS_TABLE_STATE_RESET: 'ACCOUNTS/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,5 @@
import t from 'store/types';
export const setLogin = () => ({ type: t.LOGIN_SUCCESS });
export const setLogout = () => ({ type: t.LOGOUT });
export const setStoreReset = () => ({ type: t.RESET });

View File

@@ -0,0 +1,51 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer } from 'redux-persist';
import purgeStoredState from 'redux-persist/es/purgeStoredState';
import storage from 'redux-persist/lib/storage';
import { getCookie, setCookie } from 'utils';
import t from 'store/types';
import { removeCookie } from '../../utils';
// Read stored data in cookies and merge it with the initial state.
const initialState = {
token: getCookie('token'),
organizationId: getCookie('organization_id'),
tenantId: getCookie('tenant_id'),
userId: getCookie('authenticated_user_id'),
locale: getCookie('locale'),
errors: [],
};
const STORAGE_KEY = 'bigcapital:authentication';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
[t.LOGIN_FAILURE]: (state, action) => {
state.errors = action.errors;
},
[t.LOGIN_CLEAR_ERRORS]: (state) => {
state.errors = [];
},
[t.RESET]: (state) => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);
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;

View File

@@ -0,0 +1,24 @@
import { defaultTo } from 'lodash';
import { createSelector } from '@reduxjs/toolkit';
const getCurrentOrganizationId = (state) => state.authentication.organizationId;
const getCurrentTenantId = (state) => state.authentication.tenantId;
const getOrganizationsMap = (state) => state.organizations.data;
// Retrieve organization tenant id.
export const getOrganizationTenantIdFactory = () =>
createSelector(getCurrentTenantId, (tenantId) => tenantId);
// Retrieve organization id.
export const getOrganizationIdFactory = () =>
createSelector(getCurrentOrganizationId, (tenantId) => tenantId);
// Retrieve current organization meta object.
export const getCurrentOrganizationFactory = () =>
createSelector(
getCurrentTenantId,
getOrganizationsMap,
(tenantId, organizationsMap) => {
return defaultTo(organizationsMap[tenantId], {});
},
);

View File

@@ -0,0 +1,9 @@
export default {
LOGIN_REQUEST: 'LOGIN_REQUEST',
LOGIN_SUCCESS: 'LOGIN_SUCCESS',
LOGIN_FAILURE: 'LOGIN_FAILURE',
LOGOUT: 'LOGOUT',
LOGIN_CLEAR_ERRORS: 'LOGIN_CLEAR_ERRORS',
RESET: 'RESET',
};

View File

@@ -0,0 +1,18 @@
import ApiService from 'services/ApiService';
import t from 'store/types';
export const submitBilling = ({ form }) => {
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.post('subscription/license/payment', form)
.then((response) => {
resolve(response);
})
.catch((error) => {
const { response } = error;
const { data } = response;
reject(data?.errors);
});
});
};

View File

52
src/store/createStore.js Normal file
View File

@@ -0,0 +1,52 @@
import {
createStore as createReduxStore,
applyMiddleware,
compose,
} from 'redux';
import thunkMiddleware from 'redux-thunk';
import { persistStore } from 'redux-persist';
import monitorReducerEnhancer from 'store/enhancers/monitorReducer';
import loggerMiddleware from 'middleware/logger';
import rootReducer from 'store/reducers';
import ResetMiddleware from './ResetMiddleware';
const createStoreFactory = (initialState = {}) => {
/**
|--------------------------------------------------
| Middleware Configuration
|--------------------------------------------------
*/
const middleware = [thunkMiddleware, loggerMiddleware];
/**
|--------------------------------------------------
| Store Enhancers
|--------------------------------------------------
*/
const enhancers = [monitorReducerEnhancer, ResetMiddleware];
let composeEnhancers = compose;
if (process.env.NODE_ENV === 'development') {
if (typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ === 'function') {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
}
}
/**
|--------------------------------------------------
| Store Instantiation and HMR Setup
|--------------------------------------------------
*/
const store = createReduxStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(...middleware), ...enhancers),
);
store.asyncReducers = {};
return store;
};
export const createStore = createStoreFactory;
export const store = createStoreFactory();
export const persistor = persistStore(store);

View File

@@ -0,0 +1,69 @@
import ApiService from 'services/ApiService';
import t from 'store/types';
export const submitCurrencies = ({ form }) => {
return (dispatch) => {
return ApiService.post('currencies', form);
};
};
export const deleteCurrency = ({ currency_code }) => {
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.delete(`currencies/${currency_code}`)
.then((response) => {
dispatch({ type: t.CURRENCY_CODE_DELETE, currency_code });
resolve(response);
})
.catch((error) => {
reject(error.response.data.errors || []);
});
});
};
export const editCurrency = ({ id, form }) => {
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.post(`currencies/${id}`, form)
.then((response) => {
dispatch({ type: t.CLEAR_CURRENCY_FORM_ERRORS });
resolve(response);
})
.catch((error) => {
const { response } = error;
const { data } = response;
const { errors } = data;
dispatch({ type: t.CLEAR_CURRENCY_FORM_ERRORS });
if (errors) {
dispatch({ type: t.CLEAR_CURRENCY_FORM_ERRORS, errors });
}
reject(error);
});
});
};
export const fetchCurrencies = () => {
return (dispatch) =>
new Promise((resolve, reject) => {
dispatch({
type: t.CURRENCIES_TABLE_LOADING,
loading: true,
});
ApiService.get('currencies')
.then((response) => {
dispatch({
type: t.CURRENCIES_REGISTERED_SET,
currencies: response.data.currencies,
});
dispatch({
type: t.CURRENCIES_TABLE_LOADING,
loading: false,
});
resolve(response);
})
.catch((error) => {
reject(error);
});
});
};

View File

@@ -0,0 +1,29 @@
import { createReducer } from '@reduxjs/toolkit';
import t from 'store/types';
const initialState = {
data: {},
loading: false,
};
export default createReducer(initialState, {
[t.CURRENCIES_REGISTERED_SET]: (state, action) => {
const _currencies = {};
action.currencies.forEach((currency) => {
_currencies[currency.currency_code] = currency;
});
state.data = {
...state.data,
..._currencies,
};
},
[t.CURRENCIES_TABLE_LOADING]: (state, action) => {
state.loading = action.loading;
},
[t.CURRENCY_CODE_DELETE]: (state, action) => {
if (typeof state.data[action.currency_code] !== 'undefined') {
delete state.data[action.currency_code];
}
},
});

View File

@@ -0,0 +1,22 @@
// @flow
import { createSelector } from 'reselect';
import { getItemById } from 'store/selectors';
const currenciesItemsSelector = (state) => state.currencies.data;
const currenciesCodePropSelector = (state, props) => props.currencyId;
export const getCurrenciesList = createSelector(
currenciesItemsSelector,
(currencies) => {
return Object.values(currencies);
},
);
export const getCurrencyByCode = createSelector(
currenciesItemsSelector,
currenciesCodePropSelector,
(currencies, currencyCode) => {
return getItemById(currencies, currencyCode);
},
);

View File

@@ -0,0 +1,6 @@
export default {
CURRENCIES_REGISTERED_SET: 'CURRENCIES_REGISTERED_SET',
CLEAR_CURRENCY_FORM_ERRORS: 'CLEAR_CURRENCY_FORM_ERRORS',
CURRENCIES_TABLE_LOADING: 'CURRENCIES_TABLE_LOADING',
CURRENCY_CODE_DELETE: 'CURRENCY_CODE_DELETE',
};

View File

@@ -0,0 +1,15 @@
import ApiService from 'services/ApiService';
import t from 'store/types';
export const fetchResourceFields = ({ resourceSlug }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get(`fields/resource/${resourceSlug}`).then((response) => {
dispatch({
type: t.CUSTOM_FIELDS_RESOURCE_SET,
resourceSlug: resourceSlug,
fields: response.data.fields,
});
resolve(response);
}).catch(error => { reject(error); });
});
}

View File

@@ -0,0 +1,24 @@
import {createReducer} from '@reduxjs/toolkit';
import t from 'store/types';
const initialState = {
custom_fields: {
accounts: [{
label_name: 'Label',
predefined: true,
data_type: 'text',
help_text: '123sdasd',
active: true,
}]
},
};
export default createReducer(initialState, {
[t.CUSTOM_FIELDS_RESOURCE_SET]: (state, action) => {
state.fields.custom_fields[action.resource_slug] = action.custom_field;
}
});
export const getCustomFieldsByResource = (state, resourceSlug) => {
return state.fields.custom_fields[resourceSlug];
}

View File

@@ -0,0 +1,4 @@
export default {
CUSTOM_FIELDS_RESOURCE_SET: 'CUSTOM_FIELDS_RESOURCE_SET',
};

View File

@@ -0,0 +1,63 @@
import ApiService from "services/ApiService";
import t from 'store/types';
export const submitView = ({ form }) => {
return (dispatch) => ApiService.post('views', form);
};
export const editView = ({ id, form }) => {
return (dispatch) => ApiService.post(`views/${id}`, form);
};
export const deleteView = ({ id }) => {
return (dispatch) => ApiService.delete(`views/${id}`);
};
export const fetchView = ({ id }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get(`views/${id}`).then((response) => {
dispatch({
type: t.VIEW_META_SET,
view: response.data.view,
});
resolve(response);
}).catch(error => { reject(error); });
});
};
export const fetchResourceViews = ({ resourceSlug }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get(`views/resource/${resourceSlug}`)
.then((response) => {
dispatch({
type: t.RESOURCE_VIEWS_SET,
resource: resourceSlug,
views: response.data.views,
});
dispatch({
type: t.VIEW_ITEMS_SET,
views: response.data.views,
});
resolve(response);
})
.catch((error) => { reject(error); });
});
};
export const fetchViewResource = ({ id }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get(`views/${id}/resource`).then((response) => {
dispatch({
type: t.RESOURCE_COLUMNS_SET,
columns: response.data.resource_columns,
resource_slug: response.data.resource_slug
});
dispatch({
type: t.RESOURCE_FIELDS_SET,
fields: response.data.resource_fields,
resource_slug: response.data.resource_slug,
});
resolve(response);
}).catch((error) => { reject(error); });
});
}

View File

@@ -0,0 +1,30 @@
import { createReducer } from "@reduxjs/toolkit";
import t from 'store/types';
const initialState = {
views: {},
resourceViews: {
'accounts': [],
'expenses': [],
},
viewsMeta: {},
};
export default createReducer(initialState, {
[t.VIEW_META_SET]: (state, action) => {
state.viewsMeta[action.view.id] = action.view;
},
[t.RESOURCE_VIEWS_SET]: (state, action) => {
state.resourceViews[action.resource] = action.views.map(v => v.id);
},
[t.VIEW_ITEMS_SET]: (state, action) => {
const _views = {};
action.views.forEach((view) => {
_views[view.id] = view;
});
state.views = { ...state.views, ..._views };
},
})

View File

@@ -0,0 +1,42 @@
import { createSelector } from 'reselect';
import { pickItemsFromIds } from 'store/selectors';
import { getResourceColumn } from 'store/resources/resources.reducer';
const resourceViewsIdsSelector = (state, props, resourceName) =>
state.views.resourceViews[resourceName];
const viewsSelector = (state) => state.views.views;
const viewByIdSelector = (state, props) => state.views.views[props.viewId];
const viewColumnsSelector = (state, props) => {
};
export const getResourceViews = createSelector(
resourceViewsIdsSelector,
viewsSelector,
(resourceViewsIds, views) => {
return pickItemsFromIds(views, resourceViewsIds);
},
);
export const getViewMetaFactory = () => createSelector(
viewByIdSelector,
// viewColumnsSelector,
(view, viewColumns) => {
return view;
}
);
export const getViewItemFactory = () => createSelector(
viewByIdSelector,
// viewColumnsSelector,
(view, viewColumns) => {
return view;
}
);
export const getViewPages = (resourceViews, viewId) => {
return typeof resourceViews[viewId] === 'undefined'
? {}
: resourceViews[viewId].pages;
};

View File

@@ -0,0 +1,6 @@
export default {
VIEW_META_SET: 'VIEW_META_SET',
VIEW_ITEMS_SET: 'VIEW_ITEMS_SET',
RESOURCE_VIEWS_SET: 'RESOURCE_VIEWS_SET',
};

View File

@@ -0,0 +1,17 @@
import t from 'store/types';
/**
* Sets the customers table state.
*/
export const setCustomersTableState = (queries) => {
return {
type: t.CUSTOMERS_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetCustomersTableState = () => {
return {
type: t.CUSTOMERS_TABLE_STATE_RESET,
};
}

View File

@@ -0,0 +1,33 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
// Default table query state.
export const defaultTableQueryState = {
pageSize: 12,
pageIndex: 0,
inactiveMode: false,
filterRoles: [],
viewSlug: null,
};
// initial data.
const initialState = {
tableState: defaultTableQueryState,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('CUSTOMERS', defaultTableQueryState),
});
const STORAGE_KEY = 'bigcapital:estimates';
export default persistReducer(
{
key: STORAGE_KEY,
whitelist: [],
storage,
},
reducerInstance,
);

View File

@@ -0,0 +1,25 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQueryState } from './customers.reducer';
const customerTableStateSelector = (state) => state.customers.tableState;
export const getCustomersTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
customerTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const customersTableStateChangedFactory = () =>
createDeepEqualSelector(customerTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQueryState);
});

View File

@@ -0,0 +1,4 @@
export default {
CUSTOMERS_TABLE_STATE_SET: 'CUSTOMERS/TABLE_STATE_SET',
CUSTOMERS_TABLE_STATE_RESET: 'CUSTOMERS/TABLE_STATE_RESET'
};

View File

@@ -0,0 +1,87 @@
import t from 'store/types';
export function dashboardPageTitle(pageTitle) {
return {
type: t.CHANGE_DASHBOARD_PAGE_TITLE,
pageTitle,
};
}
export function dashboardPageHint(pageHint) {
return {
type: t.CHANGE_DASHBOARD_PAGE_HINT,
pageHint,
};
}
export function openDialog(name, payload) {
return {
type: t.OPEN_DIALOG,
name: name,
payload: payload,
};
}
export function closeDialog(name, payload) {
return {
type: t.CLOSE_DIALOG,
name: name,
payload: payload,
};
}
export function openAlert(name, payload) {
return {
type: t.OPEN_ALERT,
name,
payload,
};
}
export function closeAlert(name, payload) {
return {
type: t.CLOSE_ALERT,
name,
payload,
};
}
export function openDrawer(name, payload) {
return {
type: t.OPEN_DRAWER,
name,
payload,
};
}
export function closeDrawer(name, payload) {
return {
type: t.CLOSE_DRAWER,
name,
payload,
};
}
/**
* Toggles the sidebar expend.
*/
export function toggleExpendSidebar(toggle) {
return {
type: t.SIDEBAR_EXPEND_TOGGLE,
payload: { toggle }
};
}
export function appIsLoading(toggle) {
return {
type: t.APP_IS_LOADING,
payload: { isLoading: toggle },
};
}
export function appIntlIsLoading(toggle) {
return {
type: t.APP_INTL_IS_LOADING,
payload: { isLoading: toggle },
};
}

View File

@@ -0,0 +1,130 @@
import { createReducer } from '@reduxjs/toolkit';
import { isUndefined } from 'lodash';
import t from 'store/types';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const initialState = {
pageTitle: '',
pageSubtitle: '',
pageHint: '',
preferencesPageTitle: '',
sidebarExpended: true,
dialogs: {},
alerts: {},
drawers: {},
topbarEditViewId: null,
requestsLoading: 0,
backLink: false,
appIsLoading: true,
appIntlIsLoading: true,
};
const STORAGE_KEY = 'bigcapital:dashboard';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
[t.CHANGE_DASHBOARD_PAGE_TITLE]: (state, action) => {
state.pageTitle = action.pageTitle;
},
[t.ALTER_DASHBOARD_PAGE_SUBTITLE]: (state, action) => {
state.pageSubtitle = action.pageSubtitle;
},
[t.CHANGE_DASHBOARD_PAGE_HINT]: (state, action) => {
state.pageHint = action.payload.pageHint;
},
[t.CHANGE_PREFERENCES_PAGE_TITLE]: (state, action) => {
state.preferencesPageTitle = action.pageTitle;
},
[t.OPEN_DIALOG]: (state, action) => {
state.dialogs[action.name] = {
isOpen: true,
payload: action.payload || {},
};
},
[t.CLOSE_DIALOG]: (state, action) => {
state.dialogs[action.name] = {
...state.dialogs[action.name],
isOpen: false,
};
},
[t.OPEN_ALERT]: (state, action) => {
state.alerts[action.name] = {
isOpen: true,
payload: action.payload || {},
};
},
[t.CLOSE_ALERT]: (state, action) => {
state.alerts[action.name] = {
...state.alerts[action.name],
isOpen: false,
};
},
[t.OPEN_DRAWER]: (state, action) => {
state.drawers[action.name] = {
isOpen: true,
payload: action.payload || {},
};
},
[t.CLOSE_DRAWER]: (state, action) => {
state.drawers[action.name] = {
...state.drawers[action.name],
isOpen: false,
};
},
[t.CLOSE_ALL_DIALOGS]: (state, action) => {},
[t.SET_TOPBAR_EDIT_VIEW]: (state, action) => {
state.topbarEditViewId = action.id;
},
[t.SIDEBAR_EXPEND_TOGGLE]: (state, action) => {
const { toggle } = action.payload;
state.sidebarExpended = isUndefined(toggle)
? !state.sidebarExpended
: !!toggle;
},
[t.SET_DASHBOARD_BACK_LINK]: (state, action) => {
const { backLink } = action.payload;
state.backLink = backLink;
},
[t.APP_IS_LOADING]: (state, action) => {
const { isLoading } = action.payload;
state.appIsLoading = isLoading;
},
[t.APP_INTL_IS_LOADING]: (state, action) => {
const { isLoading } = action.payload;
state.appIntlIsLoading = isLoading;
},
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);
export const getDialogPayload = (state, dialogName) => {
return typeof state.dashboard.dialogs[dialogName] !== 'undefined'
? state.dashboard.dialogs[dialogName].payload
: {};
};
export const getDialogActiveStatus = (state, dialogName) => {
return true;
};

View File

@@ -0,0 +1,40 @@
import { createSelector } from '@reduxjs/toolkit';
const dialogByNameSelector = (state, props) =>
state.dashboard.dialogs?.[props.dialogName];
export const isDialogOpenFactory = () =>
createSelector(dialogByNameSelector, (dialog) => {
return dialog && dialog.isOpen;
});
export const getDialogPayloadFactory = () =>
createSelector(dialogByNameSelector, (dialog) => {
return { ...dialog?.payload };
});
const alertByNameSelector = (state, props) =>
state.dashboard.alerts?.[props.name];
export const isAlertOpenFactory = () =>
createSelector(alertByNameSelector, (alert) => {
return alert && alert.isOpen;
});
export const getAlertPayloadFactory = () =>
createSelector(alertByNameSelector, (alert) => {
return { ...alert?.payload };
});
const drawerByNameSelector = (state, props) =>
state.dashboard.drawers?.[props.name];
export const isDrawerOpenFactory = () =>
createSelector(drawerByNameSelector, (drawer) => {
return drawer && drawer.isOpen;
});
export const getDrawerPayloadFactory = () =>
createSelector(drawerByNameSelector, (drawer) => {
return { ...drawer?.payload };
});

View File

@@ -0,0 +1,19 @@
export default {
OPEN_DIALOG: 'OPEN_DIALOG',
CLOSE_DIALOG: 'CLOSE_DIALOG',
OPEN_ALERT: 'OPEN_ALERT',
CLOSE_ALERT: 'CLOSE_ALERT',
CLOSE_ALL_DIALOGS: 'CLOSE_ALL_DIALOGS',
CLOSE_ALL_ALERTS: 'CLOSE_ALL_ALERTS',
OPEN_DRAWER: 'OPEN_DRAWER',
CLOSE_DRAWER: 'CLOSE_DRAWER',
CHANGE_DASHBOARD_PAGE_TITLE: 'CHANGE_DASHBOARD_PAGE_TITLE',
CHANGE_DASHBOARD_PAGE_HINT: 'CHANGE_DASHBOARD_PAGE_HINT',
CHANGE_PREFERENCES_PAGE_TITLE: 'CHANGE_PREFERENCES_PAGE_TITLE',
ALTER_DASHBOARD_PAGE_SUBTITLE: 'ALTER_DASHBOARD_PAGE_SUBTITLE',
SET_TOPBAR_EDIT_VIEW: 'SET_TOPBAR_EDIT_VIEW',
SIDEBAR_EXPEND_TOGGLE: 'SIDEBAR_EXPEND_TOGGLE',
SET_DASHBOARD_BACK_LINK: 'SET_DASHBOARD_BACK_LINK',
APP_IS_LOADING: 'APP_IS_LOADING',
APP_INTL_IS_LOADING: 'APP_INTL_IS_LOADING'
};

View File

@@ -0,0 +1,17 @@
const round = number => Math.round(number * 100) / 100
const monitorReducerEnhancer = createStore => (
reducer,
initialState,
enhancer
) => {
const monitoredReducer = (state, action) => {
const start = performance.now()
const newState = reducer(state, action)
const end = performance.now()
const diff = round(end - start)
console.log('reducer process time:', diff)
return newState
}
return createStore(monitoredReducer, initialState, enhancer)
}
export default monitorReducerEnhancer

View File

@@ -0,0 +1,19 @@
import t from 'store/types';
/**
* Sets global table state of the table.
* @param {object} queries
*/
export const setExpensesTableState = (queries) => {
return {
type: t.EXPENSES_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetExpensesTableState = () => {
return {
type: t.EXPENSES_TABLE_STATE_RESET,
};
};

View File

@@ -0,0 +1,36 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
// Default table query.
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
};
// Initial state.
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:expenses';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('EXPENSES', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
}
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,26 @@
import { isEqual } from 'lodash';
import { createDeepEqualSelector } from 'utils';
import { paginationLocationQuery } from 'store/selectors';
import { defaultTableQuery } from './expenses.reducer';
// Items table state selectors.
const expensesTableStateSelector = (state) => state.expenses.tableState;
// Retrive expenses table query.
export const getExpensesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
expensesTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const expensesTableStateChangedFactory = () =>
createDeepEqualSelector(expensesTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,4 @@
export default {
EXPENSES_TABLE_STATE_SET: 'EXPENSES/TABLE_STATE_SET',
EXPENSES_TABLE_STATE_RESET: 'EXPENSES/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,206 @@
import t from 'store/types';
/**
* Toggles display of the balance sheet filter drawer.
* @param {boolean} toggle
*/
export function toggleBalanceSheetFilterDrawer(toggle) {
return {
type: `${t.BALANCE_SHEET}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the trial balance sheet filter drawer.
* @param {boolean} toggle
*/
export function toggleTrialBalanceSheetFilterDrawer(toggle) {
return {
type: `${t.TRIAL_BALANCE_SHEET}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the journal sheet filter drawer.
* @param {boolean} toggle
*/
export function toggleJournalSheeetFilterDrawer(toggle) {
return {
type: `${t.JOURNAL}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the profit/loss filter drawer.
* @param {boolean} toggle
*/
export function toggleProfitLossFilterDrawer(toggle) {
return {
type: `${t.PROFIT_LOSS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the general ledger filter drawer.
* @param {boolean} toggle
*/
export function toggleGeneralLedgerFilterDrawer(toggle) {
return {
type: `${t.GENERAL_LEDGER}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the AR aging summary filter drawer.
* @param {boolean} toggle -
*/
export function toggleARAgingSummaryFilterDrawer(toggle) {
return {
type: `${t.AR_AGING_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the AP aging summary filter drawer.
* @param {boolean} toggle -
*/
export function toggleAPAgingSummaryFilterDrawer(toggle) {
return {
type: `${t.AP_AGING_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the purchases by items filter drawer.
* @param {boolean} toggle
*/
export function togglePurchasesByItemsFilterDrawer(toggle) {
return {
type: `${t.PURCHASES_BY_ITEMS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the sells by items filter drawer.
* @param {boolean} toggle
*/
export function toggleSalesByItemsFilterDrawer(toggle) {
return {
type: `${t.SALES_BY_ITEMS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the inventory valuation filter drawer.
* @param {boolean} toggle
*/
export function toggleInventoryValuationFilterDrawer(toggle) {
return {
type: `${t.INVENTORY_VALUATION}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the customers balance summary filter drawer.
* @param {boolean} toggle
*/
export function toggleCustomersBalanceSummaryFilterDrawer(toggle) {
return {
type: `${t.CUSTOMERS_BALANCE_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the vendors balance summary filter drawer.
* @param {boolean} toggle
*/
export function toggleVendorsBalanceSummaryFilterDrawer(toggle) {
return {
type: `${t.VENDORS_BALANCE_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the customers transactions filter drawer.
* @param {boolean} toggle
*/
export function toggleCustomersTransactionsFilterDrawer(toggle) {
return {
type: `${t.CUSTOMERS_TRANSACTIONS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the vendors transactions filter drawer.
* @param {boolean} toggle
*/
export function toggleVendorsTransactionsFilterDrawer(toggle) {
return {
type: `${t.VENDORS_TRANSACTIONS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggle display of the cash flow statement filter drawer.
* @param {boolean} toggle
*/
export function toggleCashFlowStatementFilterDrawer(toggle) {
return {
type: `${t.CASH_FLOW_STATEMENT}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}
/**
* Toggles display of the inventory item details filter drawer.
* @param {boolean} toggle
*/
export function toggleInventoryItemDetailsFilterDrawer(toggle) {
return {
type: `${t.INVENTORY_ITEM_DETAILS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`,
payload: {
toggle,
},
};
}

View File

@@ -0,0 +1,190 @@
import { omit, chain } from 'lodash';
import moment from 'moment';
export const mapBalanceSheetToTableRows = (accounts) => {
return accounts.map((account) => {
return {
...account,
children: mapBalanceSheetToTableRows([
...(account.children ? account.children : []),
...(account.total && account.children && account.children.length > 0
? [
{
name: `Total ${account.name}`,
row_types: ['total-row', account.section_type],
total: { ...account.total },
...(account.total_periods && {
total_periods: account.total_periods,
}),
},
]
: []),
]),
};
});
};
export const profitLossToTableRowsMapper = () => {};
export const journalToTableRowsMapper = (journal) => {
const TYPES = {
ENTRY: 'ENTRY',
TOTAL_ENTRIES: 'TOTAL_ENTRIES',
EMPTY_ROW: 'EMPTY_ROW',
};
const entriesMapper = (transaction) => {
return transaction.entries.map((entry, index) => ({
...(index === 0
? {
date: transaction.date,
reference_type: transaction.reference_type,
reference_id: transaction.reference_id,
reference_type_formatted: transaction.reference_type_formatted,
}
: {}),
rowType: TYPES.ENTRY,
...entry,
}));
};
return chain(journal)
.map((transaction) => {
const entries = entriesMapper(transaction);
return [
...entries,
{
rowType: TYPES.TOTAL_ENTRIES,
currency_code: transaction.currency_code,
credit: transaction.credit,
debit: transaction.debit,
formatted_credit: transaction.formatted_credit,
formatted_debit: transaction.formatted_debit,
},
{
rowType: TYPES.EMPTY_ROW,
},
];
})
.flatten()
.value();
};
export const generalLedgerToTableRows = (accounts) => {
return chain(accounts)
.map((account) => {
return {
name: '',
code: account.code,
rowType: 'ACCOUNT_ROW',
date: account.name,
children: [
{
...account.opening_balance,
name: 'Opening balance',
rowType: 'OPENING_BALANCE',
},
...account.transactions.map((transaction) => ({
...transaction,
name: account.name,
code: account.code,
date: moment(transaction.date).format('DD MMM YYYY'),
})),
{
...account.closing_balance,
name: 'Closing balance',
rowType: 'CLOSING_BALANCE',
},
],
};
})
.value();
};
export const ARAgingSummaryTableRowsMapper = (sheet, total) => {
const rows = [];
const mapAging = (agingPeriods) => {
return agingPeriods.reduce((acc, aging, index) => {
acc[`aging-${index}`] = aging.total.formatted_amount;
return acc;
}, {});
};
sheet.customers.forEach((customer) => {
const agingRow = mapAging(customer.aging);
rows.push({
rowType: 'customer',
name: customer.customer_name,
...agingRow,
current: customer.current.formatted_amount,
total: customer.total.formatted_amount,
});
});
if (rows.length <= 0) {
return [];
}
return [
...rows,
{
name: '',
rowType: 'total',
current: sheet.total.current.formatted_amount,
...mapAging(sheet.total.aging),
total: sheet.total.total.formatted_amount,
},
];
};
export const APAgingSummaryTableRowsMapper = (sheet, total) => {
const rows = [];
const mapAging = (agingPeriods) => {
return agingPeriods.reduce((acc, aging, index) => {
acc[`aging-${index}`] = aging.total.formatted_amount;
return acc;
}, {});
};
sheet.vendors.forEach((vendor) => {
const agingRow = mapAging(vendor.aging);
rows.push({
rowType: 'vendor',
name: vendor.vendor_name,
...agingRow,
current: vendor.current.formatted_amount,
total: vendor.total.formatted_amount,
});
});
if (rows.length <= 0) {
return [];
}
return [
...rows,
{
name: '',
rowType: 'total',
current: sheet.total.current.formatted_amount,
...mapAging(sheet.total.aging),
total: sheet.total.total.formatted_amount,
},
];
};
export const mapTrialBalanceSheetToRows = (sheet) => {
const results = [];
if (sheet.accounts) {
sheet.accounts.forEach((account) => {
results.push(account);
});
}
if (sheet.total) {
results.push({
rowType: 'total',
...sheet.total,
});
}
return results;
};

View File

@@ -0,0 +1,105 @@
import { createReducer } from '@reduxjs/toolkit';
import t from 'store/types';
// Initial state.
const initialState = {
balanceSheet: {
displayFilterDrawer: false,
},
trialBalance: {
displayFilterDrawer: false,
},
generalLedger: {
displayFilterDrawer: false,
},
journal: {
displayFilterDrawer: false,
},
profitLoss: {
displayFilterDrawer: false,
},
ARAgingSummary: {
displayFilterDrawer: false,
},
APAgingSummary: {
displayFilterDrawer: false,
},
purchasesByItems: {
displayFilterDrawer: false,
},
salesByItems: {
displayFilterDrawer: false,
},
inventoryValuation: {
displayFilterDrawer: false,
},
customersBalanceSummary: {
displayFilterDrawer: false,
},
vendorsBalanceSummary: {
displayFilterDrawer: false,
},
customersTransactions: {
displayFilterDrawer: false,
},
vendorsTransactions: {
displayFilterDrawer: false,
},
cashFlowStatement: {
displayFilterDrawer: false,
},
inventoryItemDetails: {
displayFilterDrawer: false,
},
};
/**
* Financial statement filter toggle.
*/
const financialStatementFilterToggle = (financialName, statePath) => {
return {
[`${financialName}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`]: (state, action) => {
state[statePath].displayFilterDrawer =
typeof action?.payload?.toggle !== 'undefined'
? action.payload.toggle
: !state[statePath].displayFilterDrawer;
},
};
};
export default createReducer(initialState, {
...financialStatementFilterToggle(t.BALANCE_SHEET, 'balanceSheet'),
...financialStatementFilterToggle(t.TRIAL_BALANCE_SHEET, 'trialBalance'),
...financialStatementFilterToggle(t.JOURNAL, 'journal'),
...financialStatementFilterToggle(t.GENERAL_LEDGER, 'generalLedger'),
...financialStatementFilterToggle(t.PROFIT_LOSS, 'profitLoss'),
...financialStatementFilterToggle(t.AR_AGING_SUMMARY, 'ARAgingSummary'),
...financialStatementFilterToggle(t.AP_AGING_SUMMARY, 'APAgingSummary'),
...financialStatementFilterToggle(t.PURCHASES_BY_ITEMS, 'purchasesByItems'),
...financialStatementFilterToggle(t.SALES_BY_ITEMS, 'salesByItems'),
...financialStatementFilterToggle(
t.INVENTORY_VALUATION,
'inventoryValuation',
),
...financialStatementFilterToggle(
t.CUSTOMERS_BALANCE_SUMMARY,
'customersBalanceSummary',
),
...financialStatementFilterToggle(
t.VENDORS_BALANCE_SUMMARY,
'vendorsBalanceSummary',
),
...financialStatementFilterToggle(
t.CUSTOMERS_TRANSACTIONS,
'customersTransactions',
),
...financialStatementFilterToggle(
t.VENDORS_TRANSACTIONS,
'vendorsTransactions',
),
...financialStatementFilterToggle(t.CASH_FLOW_STATEMENT, 'cashFlowStatement'),
...financialStatementFilterToggle(
t.INVENTORY_ITEM_DETAILS,
'inventoryItemDetails',
),
});

View File

@@ -0,0 +1,241 @@
import { createSelector } from 'reselect';
// Financial Statements selectors.
export const sheetByTypeSelector = (sheetType) => (state, props) => {
return state.financialStatements[sheetType];
};
export const filterDrawerByTypeSelector = (sheetType) => (state) => {
return sheetByTypeSelector(sheetType)(state)?.displayFilterDrawer;
};
export const balanceSheetFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('balanceSheet')(state);
};
export const profitLossSheetFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('profitLoss')(state);
};
export const generalLedgerFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('generalLedger')(state);
};
// Trial balance filter drawer selector.
export const trialBalanceFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('trialBalance')(state);
};
export const journalFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('journal')(state);
};
export const ARAgingSummaryFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('ARAgingSummary')(state);
};
export const APAgingSummaryFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('APAgingSummary')(state);
};
export const purchasesByItemsFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('purchasesByItems')(state);
};
export const salesByItemsFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('salesByItems')(state);
};
export const inventoryValuationFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('inventoryValuation')(state);
};
export const customerBalanceSummaryFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('customersBalanceSummary')(state);
};
export const vendorsBalanceSummaryFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('vendorsBalanceSummary')(state);
};
export const customersTransactionsFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('customersTransactions')(state);
};
export const vendorsTransactionsFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('vendorsTransactions')(state);
};
export const cashFlowStatementFilterDrawerSelector = (state) => {
return filterDrawerByTypeSelector('cashFlowStatement')(state);
};
export const inventoryItemDetailsDrawerFilter = (state) => {
return filterDrawerByTypeSelector('inventoryItemDetails')(state);
};
/**
* Retrieve balance sheet filter drawer.
*/
export const getBalanceSheetFilterDrawer = createSelector(
balanceSheetFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether trial balance sheet display filter drawer.
*/
export const getTrialBalanceSheetFilterDrawer = createSelector(
trialBalanceFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve profit/loss filter drawer.
*/
export const getProfitLossFilterDrawer = createSelector(
profitLossSheetFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether display general ledger (GL) filter drawer.
*/
export const getGeneralLedgerFilterDrawer = createSelector(
generalLedgerFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether display journal sheet filter drawer.
*/
export const getJournalFilterDrawer = createSelector(
journalFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether display AR aging summary drawer filter.
*/
export const getARAgingSummaryFilterDrawer = createSelector(
ARAgingSummaryFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether display AR aging summary drawer filter.
*/
export const getAPAgingSummaryFilterDrawer = createSelector(
APAgingSummaryFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve financial statement query by the given sheet index.
*/
export const getFinancialSheetQueryFactory = (sheetType) =>
createSelector(sheetByTypeSelector(sheetType), (sheet) => {
return sheet && sheet.query ? sheet.query : {};
});
/**
* Retrieve whether purchases by items display filter drawer.
*/
export const getPurchasesByItemsFilterDrawer = createSelector(
purchasesByItemsFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether sales by items display filter drawer.
*/
export const getSalesByItemsFilterDrawer = createSelector(
salesByItemsFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve whether sells by items display filter drawer.
*/
export const getInventoryValuationFilterDrawer = createSelector(
inventoryValuationFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve customers balance summary filter drawer.
*/
export const getCustomersBalanceSummaryFilterDrawer = createSelector(
customerBalanceSummaryFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve vendors balance summary filter drawer.
*/
export const getVendorsBalanceSummaryFilterDrawer = createSelector(
vendorsBalanceSummaryFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve customers transactions filter drawer.
*/
export const getCustomersTransactionsFilterDrawer = createSelector(
customersTransactionsFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve vendors transactions filter drawer.
*/
export const getVendorsTransactionsFilterDrawer = createSelector(
vendorsTransactionsFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve cash flow statement filter drawer.
*/
export const getCashFlowStatementFilterDrawer = createSelector(
cashFlowStatementFilterDrawerSelector,
(isOpen) => {
return isOpen;
},
);
/**
* Retrieve inventory item details filter drawer.
*/
export const getInventoryItemDetailsFilterDrawer = createSelector(
inventoryItemDetailsDrawerFilter,
(isOpen) => {
return isOpen;
},
);

View File

@@ -0,0 +1,19 @@
export default {
BALANCE_SHEET: 'BALANCE_SHEET',
TRIAL_BALANCE_SHEET: 'TRIAL_BALANCE_SHEET',
JOURNAL: 'JOURNAL',
GENERAL_LEDGER: 'GENERAL_LEDGER',
PROFIT_LOSS: 'PROFIT_LOSS',
AR_AGING_SUMMARY: 'AR_AGING_SUMMARY',
AP_AGING_SUMMARY: 'AP_AGING_SUMMARY',
DISPLAY_FILTER_DRAWER_TOGGLE: 'DISPLAY_FILTER_DRAWER_TOGGLE',
PURCHASES_BY_ITEMS: 'PURCHASES_BY_ITEMS',
SALES_BY_ITEMS: 'SALES_BY_ITEMS',
INVENTORY_VALUATION: 'INVENTORY_VALUATION',
CUSTOMERS_BALANCE_SUMMARY: 'CUSTOMERS BALANCE SUMMARY',
VENDORS_BALANCE_SUMMARY: 'VENDORS BALANCE SUMMARY',
CUSTOMERS_TRANSACTIONS: 'CUSTOMERS TRANSACTIONS',
VENDORS_TRANSACTIONS: 'VENDORS TRANSACTIONS',
CASH_FLOW_STATEMENT: 'CASH FLOW STATEMENT',
INVENTORY_ITEM_DETAILS: 'INVENTORY ITEM DETAILS',
};

View File

@@ -0,0 +1,10 @@
export const setGlobalErrors = (errors) => {
return {
type: 'GLOBAL_ERRORS_SET',
payload: {
errors,
},
};
}

View File

@@ -0,0 +1,17 @@
import { createReducer } from '@reduxjs/toolkit';
import t from 'store/types';
const initialState = {
data: {},
};
export default createReducer(initialState, {
['GLOBAL_ERRORS_SET']: (state, action) => {
const { errors } = action.payload;
state.data = {
...state.data,
...errors,
};
},
});

View File

@@ -0,0 +1,11 @@
import t from 'store/types';
/**
* Sets the inventory adjustments table state.
*/
export const setInventoryAdjustmentsTableState = (queries) => {
return {
type: t.INVENTORY_ADJUSTMENTS_TABLE_STATE_SET,
payload: { queries },
};
};

View File

@@ -0,0 +1,32 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
const initialState = {
tableState: {
pageSize: 12,
pageIndex: 0,
sortBy: [],
},
selectedRows: [],
};
const STORAGE_KEY = 'bigcapital:inventoryAdjustments';
const CONFIG = {
key: STORAGE_KEY,
whitelist: ['tableState'],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('INVENTORY_ADJUSTMENTS'),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,22 @@
import { createSelector } from '@reduxjs/toolkit';
import {
paginationLocationQuery,
} from 'store/selectors';
const inventoryAdjustmentTableState = (state) =>
state.inventoryAdjustments.tableState;
/**
* Retrieve the inventory adjustments table state.
*/
export const getInventroyAdjsTableStateFactory = () =>
createSelector(
paginationLocationQuery,
inventoryAdjustmentTableState,
(locationQuery, tableQuery) => {
return {
...locationQuery,
...tableQuery,
};
},
);

View File

@@ -0,0 +1,4 @@
export default {
INVENTORY_ADJUSTMENTS_TABLE_STATE_SET: 'INVENTORY_ADJUSTMENTS/TABLE_STATE_SET',
};

View File

@@ -0,0 +1,19 @@
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
// Items categories table state.
const itemsCategoriesTableStateSelector = (state) =>
state.itemsCategories.tableState;
// Get items categories table state marged with location query.
export const getItemsCategoriesTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
itemsCategoriesTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);

View File

@@ -0,0 +1,11 @@
import t from 'store/types';
/**
* Sets the items categories table state.
*/
export const setItemsCategoriesTableState = (queries) => {
return {
type: t.ITEMS_CATEGORIES_TABLE_STATE_SET,
payload: { queries },
};
};

View File

@@ -0,0 +1,35 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import {
createTableStateReducers,
} from 'store/tableState.reducer';
import t from 'store/types';
// Initial state.
const initialState = {
tableState: {
filterRoles: []
},
};
const STORAGE_KEY = 'bigcapital:itemCategories';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('ITEMS_CATEGORIES'),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(
CONFIG,
reducerInstance,
);

View File

@@ -0,0 +1,3 @@
export default {
ITEMS_CATEGORIES_TABLE_STATE_SET: 'ITEMS_CATEGORIES/TABLE_STATE_SET',
};

View File

@@ -0,0 +1,38 @@
import t from 'store/types';
import { createReducer } from '@reduxjs/toolkit';
const initialState = {
tableState: {
filterRoles: [],
},
categories: {},
loading: false,
};
export default createReducer(initialState, {
[t.ITEMS_CATEGORY_LIST_SET]: (state, action) => {
const _categories = {};
action.categories.forEach(category => {
_categories[category.id] = category;
});
state.categories = {
...state.categories,
..._categories
};
},
[t.CATEGORY_DELETE]: (state, action) => {
const { id } = action.payload;
const categories = { ...state.categories };
if (typeof categories[id] !== 'undefined') {
delete categories[id];
state.categories = categories;
}
}
});
export const getCategoryId = (state, id) => {
return state.itemCategories.categories[id] || {};
};

View File

@@ -0,0 +1,17 @@
import t from 'store/types';
export const setItemsTableState = (queries) => {
return {
type: t.ITEMS_TABLE_STATE_SET,
payload: { queries },
};
};
export const resetItemsTableState = () => {
return {
type: t.ITEMS_TABLE_STATE_RESET,
};
}
export const setSelectedRowsItems = () => {};

View File

@@ -0,0 +1,39 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
inactiveMode: false,
viewSlug: null,
};
const initialState = {
tableState: defaultTableQuery,
selectedRows: [],
};
const STORAGE_KEY = 'bigcapital:items';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('ITEMS', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(
CONFIG,
reducerInstance,
);

View File

@@ -0,0 +1,25 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './items.reducer';
const itemsTableStateSelector = (state) => state.items.tableState;
// Get items table state marged with location query.
export const getItemsTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
itemsTableStateSelector,
(locationQuery, tableState) => {
return {
...locationQuery,
...tableState,
};
},
);
export const isItemsTableStateChangedFactory = () =>
createDeepEqualSelector(itemsTableStateSelector, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,5 @@
export default {
ITEMS_TABLE_STATE_SET: 'ITEMS/TABLE_STATE_SET',
ITEMS_TABLE_STATE_RESET: 'ITEMS/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,47 @@
export const journalNumberChangedReducer = (type) => ({
[type]: (state, action) => {
const { isChanged } = action.payload;
state.journalNumberChanged = isChanged;
},
});
export const viewPaginationSetReducer = (type) => ({
[type]: (state, action) => {
const { pagination, customViewId } = action.payload;
const mapped = {
pageSize: parseInt(pagination.page_size, 10),
page: parseInt(pagination.page, 10),
total: parseInt(pagination.total, 10),
};
const paginationMeta = {
...mapped,
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
pageIndex: Math.max(mapped.page - 1, 0),
};
state.views = {
...state.views,
[customViewId]: {
...(state.views?.[customViewId] || {}),
paginationMeta,
},
};
},
});
export const createTableQueryReducers = (RESOURCE_NAME) => ({
[`${RESOURCE_NAME}/TABLE_QUERY_SET`]: (state, action) => {
state.tableQuery = {
...state.tableQuery,
[state.key]: action.payload.value,
};
},
[`${RESOURCE_NAME}/TABLE_QUERIES_ADD`]: (state, action) => {
state.tableQuery = {
...state.tableQuery,
...action.payload.queries,
};
},
});

23
src/store/localStorage.js Normal file
View File

@@ -0,0 +1,23 @@
const LOCAL_STORAGE_NAMESPACE = 'application_state';
export const loadState = () => {
try {
const serializedState = localStorage.getItem(LOCAL_STORAGE_NAMESPACE);
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch(error) {
return undefined;
}
};
export const saveState = (state) => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem(LOCAL_STORAGE_NAMESPACE, serializedState);
} catch (error) {
throw new Error('Something want wrong');
}
}

View File

@@ -0,0 +1,8 @@
import t from 'store/types';
export const setManualJournalsTableState = (queries) => {
return {
type: t.MANUAL_JOURNALS_TABLE_STATE_SET,
payload: { queries },
};
};

View File

@@ -0,0 +1,34 @@
import { createReducer } from '@reduxjs/toolkit';
import { persistReducer, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createTableStateReducers } from 'store/tableState.reducer';
import t from 'store/types';
export const defaultTableQuery = {
pageSize: 12,
pageIndex: 0,
filterRoles: [],
viewSlug: null,
}
const initialState = {
tableState: defaultTableQuery,
};
const STORAGE_KEY = 'bigcapital:manualJournals';
const CONFIG = {
key: STORAGE_KEY,
whitelist: [],
storage,
};
const reducerInstance = createReducer(initialState, {
...createTableStateReducers('MANUAL_JOURNALS', defaultTableQuery),
[t.RESET]: () => {
purgeStoredState(CONFIG);
},
});
export default persistReducer(CONFIG, reducerInstance);

View File

@@ -0,0 +1,25 @@
import { isEqual } from 'lodash';
import { paginationLocationQuery } from 'store/selectors';
import { createDeepEqualSelector } from 'utils';
import { defaultTableQuery } from './manualJournals.reducers';
const manualJournalsTableState = (state) => state.manualJournals.tableState;
// Retrieve manual jouranls table state.
export const getManualJournalsTableStateFactory = () =>
createDeepEqualSelector(
paginationLocationQuery,
manualJournalsTableState,
(locationQuery, tableQuery) => {
return {
...locationQuery,
...tableQuery,
};
},
);
export const manualJournalTableStateChangedFactory = () =>
createDeepEqualSelector(manualJournalsTableState, (tableState) => {
return !isEqual(tableState, defaultTableQuery);
});

View File

@@ -0,0 +1,4 @@
export default {
MANUAL_JOURNALS_TABLE_STATE_SET: 'MANUAL_JOURNALS/TABLE_STATE_SET',
MANUAL_JOURNALS_TABLE_STATE_RESET: 'MANUAL_JOURNALS/TABLE_STATE_RESET',
};

View File

@@ -0,0 +1,13 @@
import ApiService from "services/ApiService"
export const submitMedia = ({ form, config }) => {
return (dispatch) => {
return ApiService.post('media/upload', form, config);
};
};
export const deleteMedia = ({ ids }) => {
return (dispatch) => {
return ApiService.delete('media', { params: { ids } });
}
};

View File

View File

View File

@@ -0,0 +1,41 @@
import ApiService from 'services/ApiService';
import t from 'store/types';
export const setOrganizations = (organizations) => {
return {
type: t.ORGANIZATIONS_LIST_SET,
payload: {
organizations,
},
};
};
export const fetchOrganizations = () => (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);
});
});
export const setOrganizationSetupCompleted =
(congrats) => (dispatch, getState) => {
const tenantId = getState().authentication.tenantId;
dispatch({
type: t.SET_ORGANIZATION_CONGRATS,
payload: {
tenantId,
congrats,
},
});
};

View File

@@ -0,0 +1,39 @@
import { createReducer } from '@reduxjs/toolkit';
import { omit } from 'lodash';
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] = {
...state.data[organization.id],
...organization.metadata,
...omit(organization, ['metadata']),
};
_dataByOrganizationId[organization.organization_id] = organization.id;
});
state.data = _data;
state.byOrganizationId = _dataByOrganizationId;
},
[t.SET_ORGANIZATION_CONGRATS]: (state, action) => {
const { tenantId, congrats } = action.payload;
state.data[tenantId] = {
...(state.data[tenantId] || {}),
is_congrats: !!congrats,
};
}
})
export default reducer;

View File

@@ -0,0 +1,39 @@
import { createSelector } from '@reduxjs/toolkit';
const organizationSelector = (state, props) => {
const tenantId = state.organizations.byOrganizationId[props.organizationId];
return state.organizations.data[tenantId];
};
export const getOrganizationByIdFactory = () =>
createSelector(organizationSelector, (organization) => organization);
export const isOrganizationSeededFactory = () =>
createSelector(organizationSelector, (organization) => {
return !!organization?.seeded_at;
});
export const isOrganizationBuiltFactory = () =>
createSelector(organizationSelector, (organization) => {
return !!organization?.initialized_at;
});
export const isOrganizationReadyFactory = () =>
createSelector(organizationSelector, (organization) => {
return organization?.is_ready;
});
export const isOrganizationSubscribedFactory = () =>
createSelector(organizationSelector, (organization) => {
return organization?.subscriptions?.length > 0;
});
export const isOrganizationCongratsFactory = () =>
createSelector(organizationSelector, (organization) => {
return !!organization?.is_congrats;
});
export const isOrganizationBuildRunningFactory = () =>
createSelector(organizationSelector, (organization) => {
return !!organization?.is_build_running;
});

View File

@@ -0,0 +1,7 @@
export default {
ORGANIZATION_SET: 'ORGANIZATION_SET',
ORGANIZATIONS_LIST_SET: 'ORGANIZATIONS_LIST_SET',
SET_ORGANIZATION_CONGRATS: 'SET_ORGANIZATION_CONGRATS',
ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES: 'ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES'
};

View File

@@ -0,0 +1,33 @@
import { connect } from 'react-redux';
export default (mapState) => {
const mapStateToProps = (state, props) => {
const {
isOrganizationSetupCompleted,
isOrganizationReady,
isSubscriptionActive,
isOrganizationBuildRunning
} = props;
const condits = {
isCongratsStep: isOrganizationSetupCompleted,
isSubscriptionStep: !isSubscriptionActive,
isInitializingStep: isOrganizationBuildRunning,
isOrganizationStep: !isOrganizationReady && !isOrganizationBuildRunning,
};
const scenarios = [
{ condition: condits.isSubscriptionStep, step: 'subscription' },
{ condition: condits.isOrganizationStep, step: 'organization' },
{ condition: condits.isInitializingStep, step: 'initializing' },
{ condition: condits.isCongratsStep, step: 'congrats' },
];
const setupStep = scenarios.find((scenario) => scenario.condition);
const mapped = {
...condits,
setupStepId: setupStep?.step,
setupStepIndex: scenarios.indexOf(setupStep) + 1,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

View File

@@ -0,0 +1,5 @@
import t from 'store/types';
export const initSubscriptionPlans = () => ({
type: t.INIT_SUBSCRIPTION_PLANS
});

View File

@@ -0,0 +1,112 @@
import { createReducer } from '@reduxjs/toolkit';
import intl from 'react-intl-universal';
import t from 'store/types';
const getSubscriptionPeriods = () => [
{
slug: 'month',
label: intl.get('plan.monthly'),
},
{
slug: 'year',
label: intl.get('plan.yearly'),
},
];
const getSubscriptionPlans = () => [
{
name: intl.get('plan.essential.title'),
slug: 'essentials',
description: [
intl.get('plan.feature.sale_purchase_invoice'),
intl.get('plan.feature.receivable_payable_accounts'),
intl.get('plan.feature.expenses_tracking'),
intl.get('plan.feature.manual_journal'),
intl.get('plan.feature.financial_reports'),
intl.get('plan.feature.one_user_with_accountant'),
],
price: '100',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '100'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
currencyCode: 'LYD',
},
{
name: intl.get('plan.professional.title'),
slug: 'plus',
description: [
intl.get('plan.feature.all_capital_essential'),
intl.get('plan.feature.multi_currency'),
intl.get('plan.feature.purchase_sell_orders'),
intl.get('plan.feature.multi_inventory_managment'),
intl.get('plan.feature.three_users'),
intl.get('plan.feature.advanced_financial_reports'),
],
price: '200',
currencyCode: 'LYD',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '200'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
},
{
name: intl.get('plan.plus.title'),
slug: 'enterprise',
description: [
intl.get('plan.feture.all_capital_professional_features'),
intl.get('plan.feature.tracking_multi_locations'),
intl.get('plan.feature.projects_accounting'),
intl.get('plan.feature.accounting_dimensions'),
],
price: '300',
currencyCode: 'LYD',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '300'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
},
];
const initialState = {
plans: [],
periods: [],
};
export default createReducer(initialState, {
/**
* Initialize the subscription plans.
*/
[t.INIT_SUBSCRIPTION_PLANS]: (state) => {
const plans = getSubscriptionPlans();
const periods = getSubscriptionPeriods();
state.plans = plans;
state.periods = periods;
},
});

View File

@@ -0,0 +1,19 @@
import { createSelector } from 'reselect';
const plansSelector = (state) => state.plans.plans;
const planSelector = (state, props) => state.plans.plans
.find((plan) => plan.slug === props.planSlug);
// Retrieve manual jounral current page results.
export const getPlansSelector = () => createSelector(
plansSelector,
(plans) => {
return plans;
},
);
// Retrieve plan details.
export const getPlanSelector = () => createSelector(
planSelector,
(plan) => plan,
)

View File

@@ -0,0 +1,4 @@
export default {
INIT_SUBSCRIPTION_PLANS: 'INIT_SUBSCRIPTION_PLANS',
};

View File

@@ -0,0 +1,26 @@
import ApiService from "services/ApiService";
import t from 'store/types';
export const savePreferences = ({ options }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.post('options', { options }).then((response) => {
dispatch({
type: t.OPTIONS_SET,
options: response.data.options,
});
resolve(response);
}).catch(error => { reject(error); });
});
};
export const fetchPreferences = () => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get('options').then((response) => {
dispatch({
type: t.OPTIONS.SET,
options: response.data.options,
});
resolve(response);
}).catch(error => { reject(error); });
})
}

Some files were not shown because too many files have changed in this diff Show More