mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
- feat: Update react-query package to V 2.1.1.
- feat: Favicon setup. - feat: Fix accounts inactivate/activate 1 account. - feat: Seed accounts, expenses and manual journals resource fields. - feat: Validate make journal receivable/payable without contact. - feat: Validate make journal contact without receivable or payable. - feat: More components abstractions. - feat: Use reselect.js to memorize components properties. - fix: Journal type of manual journal. - fix: Sidebar style optimization. - fix: Data-table check-box style optimization. - fix: Data-table spinner style dimensions. - fix: Submit journal with contact_id and contact_type.
This commit is contained in:
10
client/src/store/ExchangeRate/exchange.selector.js
Normal file
10
client/src/store/ExchangeRate/exchange.selector.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
const exchangeRateItemsSelector = state => state.exchangeRates.exchangeRates;
|
||||
|
||||
export const getExchangeRatesList = createSelector(
|
||||
exchangeRateItemsSelector,
|
||||
(exchangeRateItems) => {
|
||||
return Object.values(exchangeRateItems);
|
||||
},
|
||||
)
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { pickItemsFromIds } from 'store/selectors';
|
||||
import { pickItemsFromIds, getItemById } from 'store/selectors';
|
||||
|
||||
const accountsViewsSelector = (state) => state.accounts.views;
|
||||
const accountsDataSelector = (state) => state.accounts.items;
|
||||
const accountsCurrentViewSelector = (state) => state.accounts.currentViewId;
|
||||
const accountIdPropSelector = (state, props) => props.accountId;
|
||||
|
||||
export const getAccountsItems = createSelector(
|
||||
accountsViewsSelector,
|
||||
@@ -17,3 +18,11 @@ export const getAccountsItems = createSelector(
|
||||
: [];
|
||||
},
|
||||
);
|
||||
|
||||
export const getAccountById = createSelector(
|
||||
accountsDataSelector,
|
||||
accountIdPropSelector,
|
||||
(accountsItems, accountId) => {
|
||||
return getItemById(accountsItems, accountId);
|
||||
}
|
||||
);
|
||||
@@ -6,7 +6,10 @@ const resourceViewsIdsSelector = (state, props, resourceName) =>
|
||||
state.views.resourceViews[resourceName] || [];
|
||||
|
||||
const viewsSelector = (state) => state.views.views;
|
||||
const viewByIdSelector = (state, props) => state.views.viewsMeta[props.viewId] || {};
|
||||
const viewByIdSelector = (state, props) => state.views.views[props.viewId] || {};
|
||||
|
||||
const viewColumnsSelector = (state, props) => {
|
||||
};
|
||||
|
||||
export const getResourceViews = createSelector(
|
||||
resourceViewsIdsSelector,
|
||||
@@ -16,22 +19,21 @@ export const getResourceViews = createSelector(
|
||||
},
|
||||
);
|
||||
|
||||
export const getViewMeta = (state, viewId) => {
|
||||
const view = { ...state.views.viewsMeta[viewId] } || {};
|
||||
|
||||
if (view.columns) {
|
||||
view.columns = view.columns.map((column) => {
|
||||
return {
|
||||
...getResourceColumn(state, column.field_id),
|
||||
};
|
||||
});
|
||||
export const getViewMetaFactory = () => createSelector(
|
||||
viewByIdSelector,
|
||||
// viewColumnsSelector,
|
||||
(view, viewColumns) => {
|
||||
return view;
|
||||
}
|
||||
return view;
|
||||
};
|
||||
);
|
||||
|
||||
export const getViewItem = (state, viewId) => {
|
||||
return state.views.views[viewId] || {};
|
||||
};
|
||||
export const getViewItemFactory = () => createSelector(
|
||||
viewByIdSelector,
|
||||
// viewColumnsSelector,
|
||||
(view, viewColumns) => {
|
||||
return view;
|
||||
}
|
||||
);
|
||||
|
||||
export const getViewPages = (resourceViews, viewId) => {
|
||||
return typeof resourceViews[viewId] === 'undefined'
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { createSelector } from "@reduxjs/toolkit";
|
||||
|
||||
const dialogByNameSelector = (dialogName) => (state) => state.dashboard.dialogs?.[dialogName];
|
||||
|
||||
const dialogByNameSelector = (state, props) => state.dashboard.dialogs[props.name];
|
||||
|
||||
export const isDialogOpen = createSelector(
|
||||
dialogByNameSelector,
|
||||
export const isDialogOpenFactory = (dialogName) => createSelector(
|
||||
dialogByNameSelector(dialogName),
|
||||
(dialog) => {
|
||||
return dialog && dialog.isOpen;
|
||||
},
|
||||
);
|
||||
|
||||
export const getDialogPayload = createSelector(
|
||||
dialogByNameSelector,
|
||||
export const getDialogPayloadFactory = (dialogName) => createSelector(
|
||||
dialogByNameSelector(dialogName),
|
||||
(dialog) => {
|
||||
return dialog?.payload;
|
||||
return { ...dialog?.payload };
|
||||
},
|
||||
);
|
||||
@@ -19,6 +19,7 @@ export const fetchExpensesTable = ({ query } = {}) => {
|
||||
type: t.EXPENSES_PAGE_SET,
|
||||
payload: {
|
||||
expenses: response.data.expenses.results,
|
||||
pagination: response.data.expenses.pagination,
|
||||
customViewId: response.data.customViewId || -1,
|
||||
},
|
||||
});
|
||||
@@ -28,6 +29,13 @@ export const fetchExpensesTable = ({ query } = {}) => {
|
||||
expenses: response.data.expenses.results,
|
||||
}
|
||||
});
|
||||
dispatch({
|
||||
type: t.EXPENSES_PAGINATION_SET,
|
||||
payload: {
|
||||
pagination: response.data.expenses.pagination,
|
||||
customViewId: response.data.customViewId || -1,
|
||||
}
|
||||
});
|
||||
dispatch({
|
||||
type: t.EXPENSES_TABLE_LOADING,
|
||||
payload: {
|
||||
|
||||
@@ -7,6 +7,10 @@ const initialState = {
|
||||
items: {},
|
||||
views: {},
|
||||
loading: false,
|
||||
tableQuery: {
|
||||
page_size: 4,
|
||||
page: 1,
|
||||
},
|
||||
currentViewId: -1,
|
||||
};
|
||||
|
||||
@@ -46,13 +50,42 @@ const reducer = createReducer(initialState, {
|
||||
},
|
||||
|
||||
[t.EXPENSES_PAGE_SET]: (state, action) => {
|
||||
const { customViewId, expenses } = action.payload;
|
||||
const { customViewId, expenses, pagination } = action.payload;
|
||||
|
||||
const viewId = customViewId || -1;
|
||||
const view = state.views[viewId] || {};
|
||||
|
||||
state.views[viewId] = {
|
||||
...view,
|
||||
ids: expenses.map((i) => i.id),
|
||||
pages: {
|
||||
...(state.views?.[viewId]?.pages || {}),
|
||||
[pagination.page]: {
|
||||
ids: expenses.map((i) => i.id),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
[t.EXPENSES_PAGINATION_SET]: (state, action) => {
|
||||
const { pagination, customViewId } = action.payload;
|
||||
|
||||
const mapped = {
|
||||
pageSize: parseInt(pagination.pageSize, 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,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -1,19 +1,53 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { pickItemsFromIds } from 'store/selectors';
|
||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||
|
||||
const expensesViewsSelector = state => state.expenses.views;
|
||||
const expensesItemsSelector = state => state.expenses.items;
|
||||
const expensesCurrentViewSelector = state => state.expenses.currentViewId;
|
||||
const expensesTableQuery = state => state.expenses.tableQuery;
|
||||
|
||||
export const getExpensesItems = createSelector(
|
||||
expensesViewsSelector,
|
||||
export const getExpensesTableQuery = createSelector(
|
||||
paginationLocationQuery,
|
||||
expensesTableQuery,
|
||||
(locationQuery, tableQuery) => {
|
||||
return {
|
||||
...locationQuery,
|
||||
...tableQuery,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const expensesPageSelector = (state, props, query) => {
|
||||
const viewId = state.expenses.currentViewId;
|
||||
return state.expenses.views?.[viewId]?.pages?.[query.page];
|
||||
};
|
||||
|
||||
const expensesItemsSelector = (state) => state.expenses.items;
|
||||
|
||||
export const getExpensesCurrentPageFactory = () => createSelector(
|
||||
expensesPageSelector,
|
||||
expensesItemsSelector,
|
||||
expensesCurrentViewSelector,
|
||||
(expensesViews, expensesItems, currentViewId) => {
|
||||
const expensesView = expensesViews[currentViewId || -1];
|
||||
|
||||
return (typeof expensesView === 'object')
|
||||
? (pickItemsFromIds(expensesItems, expensesView.ids) || [])
|
||||
(expensesPage, expensesItems) => {
|
||||
return typeof expensesPage === 'object'
|
||||
? pickItemsFromIds(expensesItems, expensesPage.ids) || []
|
||||
: [];
|
||||
},
|
||||
);
|
||||
|
||||
const expenseByIdSelector = (state, props) => state.expenses.items[props.expenseId];
|
||||
|
||||
export const getExpenseByIdFactory = () => createSelector(
|
||||
expenseByIdSelector,
|
||||
(expense) => {
|
||||
return expense;
|
||||
}
|
||||
);
|
||||
|
||||
const manualJournalsPaginationSelector = (state, props) => {
|
||||
const viewId = state.expenses.currentViewId;
|
||||
return state.expenses.views?.[viewId];
|
||||
};
|
||||
|
||||
export const getExpensesPaginationMetaFactory = () => createSelector(
|
||||
manualJournalsPaginationSelector,
|
||||
(expensesPage) => {
|
||||
return expensesPage?.paginationMeta || {};
|
||||
},
|
||||
);
|
||||
@@ -9,4 +9,5 @@ export default {
|
||||
EXPENSES_TABLE_LOADING: 'EXPENSES_TABLE_LOADING',
|
||||
EXPENSES_PAGE_SET: 'EXPENSES_PAGE_SET',
|
||||
EXPENSES_ITEMS_SET: 'EXPENSES_ITEMS_SET',
|
||||
EXPENSES_PAGINATION_SET: 'EXPENSES_PAGINATION_SET',
|
||||
};
|
||||
|
||||
18
client/src/store/itemCategories/ItemsCategories.selectors.js
Normal file
18
client/src/store/itemCategories/ItemsCategories.selectors.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { getItemById } from 'store/selectors';
|
||||
|
||||
const itemsCateogoriesDataSelector = (state) => state.itemCategories.categories;
|
||||
const itemCategoryIdFromProps = (state, props) => props.itemCategoryId;
|
||||
|
||||
export const getItemsCategoriesListFactory = () =>
|
||||
createSelector(itemsCateogoriesDataSelector, (itemsCategories) => {
|
||||
return Object.values(itemsCategories);
|
||||
});
|
||||
|
||||
export const getItemCategoryByIdFactory = () => createSelector(
|
||||
itemsCateogoriesDataSelector,
|
||||
itemCategoryIdFromProps,
|
||||
(itemsCategories, itemCategoryid) => {
|
||||
return getItemById(itemsCategories, itemCategoryid);
|
||||
},
|
||||
);
|
||||
@@ -1,3 +1,4 @@
|
||||
import { omit } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import t from 'store/types';
|
||||
|
||||
@@ -107,19 +108,27 @@ export const publishManualJournal = ({ id }) => {
|
||||
export const fetchManualJournalsTable = ({ query } = {}) => {
|
||||
return (dispatch, getState) =>
|
||||
new Promise((resolve, reject) => {
|
||||
let pageQuery = getState().manualJournals.tableQuery;
|
||||
|
||||
if (pageQuery.filter_roles) {
|
||||
pageQuery = {
|
||||
...omit(pageQuery, ['filter_roles']),
|
||||
stringified_filter_roles: JSON.stringify(pageQuery.filter_roles) || '',
|
||||
};
|
||||
}
|
||||
dispatch({
|
||||
type: t.MANUAL_JOURNALS_TABLE_LOADING,
|
||||
loading: true,
|
||||
});
|
||||
ApiService.get('accounting/manual-journals', {
|
||||
params: { ...query },
|
||||
params: { ...pageQuery, ...query },
|
||||
})
|
||||
.then((response) => {
|
||||
dispatch({
|
||||
type: t.MANUAL_JOURNALS_PAGE_SET,
|
||||
payload: {
|
||||
manualJournals: response.data.manualJournals.results,
|
||||
customViewId: response.data.customViewId || -1,
|
||||
customViewId: response.data.manualJournals?.viewMeta?.customViewId || -1,
|
||||
pagination: response.data.manualJournals.pagination,
|
||||
}
|
||||
});
|
||||
@@ -131,6 +140,7 @@ export const fetchManualJournalsTable = ({ query } = {}) => {
|
||||
type: 'MANUAL_JOURNALS_PAGINATION_SET',
|
||||
payload: {
|
||||
pagination: response.data.manualJournals.pagination,
|
||||
customViewId: response.data.manualJournals?.viewMeta?.customViewId || -1,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
|
||||
@@ -30,7 +30,7 @@ const reducer = createReducer(initialState, {
|
||||
|
||||
[t.MANUAL_JOURNAL_PUBLISH]: (state, action) => {
|
||||
const { id } = action.payload;
|
||||
const item = state.items[id] || {};
|
||||
const item = state.items[id] || {}
|
||||
|
||||
state.items[id] = { ...item, status: 1 };
|
||||
},
|
||||
@@ -72,7 +72,8 @@ const reducer = createReducer(initialState, {
|
||||
},
|
||||
|
||||
[t.MANUAL_JOURNALS_SET_CURRENT_VIEW]: (state, action) => {
|
||||
state.currentViewId = action.currentViewId;
|
||||
const { currentViewId } = action.payload;
|
||||
state.currentViewId = currentViewId;
|
||||
},
|
||||
|
||||
[t.MANUAL_JOURNAL_REMOVE]: (state, action) => {
|
||||
@@ -93,19 +94,26 @@ const reducer = createReducer(initialState, {
|
||||
},
|
||||
|
||||
[t.MANUAL_JOURNALS_PAGINATION_SET]: (state, action) => {
|
||||
const { pagination } = action.payload;
|
||||
const { pagination, customViewId } = action.payload;
|
||||
const mapped = {
|
||||
pageSize: parseInt(pagination.pageSize, 10),
|
||||
page: parseInt(pagination.page, 10),
|
||||
total: parseInt(pagination.total, 10),
|
||||
};
|
||||
|
||||
state.paginationMeta = {
|
||||
...state.paginationMeta,
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,44 @@
|
||||
import { pickItemsFromIds } from 'store/selectors';
|
||||
import { createSelector } from 'reselect';
|
||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||
|
||||
export const getManualJournalsItems = (state, viewId, pageNumber) => {
|
||||
const accountsViewPages = state.manualJournals.views[viewId || -1];
|
||||
const accountsView = accountsViewPages?.pages?.[pageNumber]?.ids || {};
|
||||
const accountsItems = state.manualJournals.items;
|
||||
|
||||
return typeof accountsView === 'object'
|
||||
? pickItemsFromIds(accountsItems, accountsView) || []
|
||||
: [];
|
||||
const manualJournalsPageSelector = (state, props, query) => {
|
||||
const viewId = state.manualJournals.currentViewId;
|
||||
return state.manualJournals.views?.[viewId]?.pages?.[query.page];
|
||||
};
|
||||
|
||||
const manualJournalsPaginationSelector = (state, props) => {
|
||||
const viewId = state.manualJournals.currentViewId;
|
||||
return state.manualJournals.views?.[viewId];
|
||||
};
|
||||
|
||||
const manualJournalsTableQuery = (state) => state.manualJournals.tableQuery;
|
||||
const manualJournalsDataSelector = (state) => state.manualJournals.items;
|
||||
|
||||
|
||||
export const getManualJournalsItems = createSelector(
|
||||
manualJournalsPageSelector,
|
||||
manualJournalsDataSelector,
|
||||
(manualJournalsPage, manualJournalsItems) => {
|
||||
return typeof manualJournalsPage === 'object'
|
||||
? pickItemsFromIds(manualJournalsItems, manualJournalsPage.ids) || []
|
||||
: [];
|
||||
},
|
||||
);
|
||||
|
||||
export const getManualJournalsPagination = createSelector(
|
||||
manualJournalsPaginationSelector,
|
||||
(manualJournalsPage) => {
|
||||
return manualJournalsPage?.paginationMeta || {};
|
||||
},
|
||||
);
|
||||
|
||||
export const getManualJournalsTableQuery = createSelector(
|
||||
paginationLocationQuery,
|
||||
manualJournalsTableQuery,
|
||||
(locationQuery, tableQuery) => {
|
||||
return {
|
||||
...locationQuery,
|
||||
...tableQuery,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {pick, at} from 'lodash';
|
||||
import { pick, at, mapValues } from 'lodash';
|
||||
|
||||
export const getItemById = (items, id) => {
|
||||
return items[id] || null;
|
||||
@@ -33,4 +33,18 @@ export const getAllResults = (items, pagination, name) => {
|
||||
}
|
||||
}
|
||||
return Object.values(pick(items || [], allPagesIds))
|
||||
}
|
||||
|
||||
export const paginationLocationQuery = (state, props) => {
|
||||
const queryParams = props.location
|
||||
? new URLSearchParams(props.location.search)
|
||||
: null;
|
||||
|
||||
const queryParamsKeys = ['page_size', 'page'];
|
||||
|
||||
return queryParams
|
||||
? mapValues(pick(Object.fromEntries(queryParams), queryParamsKeys), (v) =>
|
||||
parseInt(v, 10),
|
||||
)
|
||||
: null;
|
||||
}
|
||||
Reference in New Issue
Block a user