Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ahmed Bouhuolia
2020-10-24 12:41:24 +02:00
14 changed files with 135 additions and 76 deletions

View File

@@ -0,0 +1,49 @@
import React, { useCallback } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { ListSelect } from 'components';
import { MenuItem } from '@blueprintjs/core';
export default function CategoriesSelectList({
categoriesList,
selecetedCategoryId,
defaultSelectText = <T id={'select_category'} />,
onCategorySelected,
...restProps
}) {
// Filter Items Category
const filterItemCategory = (query, item, _index, exactMatch) => {
const normalizedTitle = item.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return `${item.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}
};
const handleItemCategorySelected = useCallback(
(ItemCategory) => onCategorySelected && onCategorySelected(ItemCategory),
[],
);
const categoryItem = useCallback(
(item, { handleClick }) => (
<MenuItem key={item.id} text={item.name} onClick={handleClick} />
),
[],
);
return (
<ListSelect
items={categoriesList}
selectedItemProp={'id'}
selectedItem={selecetedCategoryId}
labelProp={'name'}
defaultText={defaultSelectText}
onItemSelect={handleItemCategorySelected}
itemPredicate={filterItemCategory}
itemRenderer={categoryItem}
{...restProps}
/>
);
}

View File

@@ -25,6 +25,8 @@ import Dialog from './Dialog/Dialog';
import DialogContent from './Dialog/DialogContent'; import DialogContent from './Dialog/DialogContent';
import DialogSuspense from './Dialog/DialogSuspense'; import DialogSuspense from './Dialog/DialogSuspense';
import InputPrependButton from './Forms/InputPrependButton'; import InputPrependButton from './Forms/InputPrependButton';
import CategoriesSelectList from './CategoriesSelectList';
const Hint = FieldHint; const Hint = FieldHint;
export { export {
@@ -55,5 +57,6 @@ export {
Dialog, Dialog,
DialogContent, DialogContent,
DialogSuspense, DialogSuspense,
InputPrependButton InputPrependButton,
}; CategoriesSelectList
};

View File

@@ -17,6 +17,7 @@ function Customer({
formik, formik,
//#withCustomersActions //#withCustomersActions
requestFetchCustomers, requestFetchCustomers,
requestFetchCustomer,
}) { }) {
const { id } = useParams(); const { id } = useParams();
const history = useHistory(); const history = useHistory();
@@ -26,11 +27,12 @@ function Customer({
requestFetchCustomers({}), requestFetchCustomers({}),
); );
// Handle fetch customer details. // Handle fetch customer details.
const fetchCustomer= useQuery(['customer', id], () => const fetchCustomer = useQuery(
requestFetchCustomers(), ['customer', id],
{ enabled: !!id }, (key, customerId) => requestFetchCustomer(customerId),
{ enabled: id && id },
); );
const handleFormSubmit = useCallback( const handleFormSubmit = useCallback(
(payload) => { (payload) => {
payload.redirect && history.push('/customers'); payload.redirect && history.push('/customers');

View File

@@ -1,20 +1,21 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
fetchCustomers, fetchCustomers,
fetchCustomer,
submitCustomer, submitCustomer,
editCustomer, editCustomer,
deleteCustomer, deleteCustomer,
deleteBulkCustomers deleteBulkCustomers,
} from 'store/customers/customers.actions'; } from 'store/customers/customers.actions';
import t from 'store/types'; import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({ export const mapDispatchToProps = (dispatch) => ({
requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })), requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })),
requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })), requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })),
requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})), requestDeleteBulkCustomers: (ids) => dispatch(deleteBulkCustomers({ ids })),
requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })), requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })),
requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })), requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })),
requestFetchCustomer: (id) => dispatch(fetchCustomer({ id })),
addCustomersTableQueries: (queries) => addCustomersTableQueries: (queries) =>
dispatch({ dispatch({
type: t.CUSTOMERS_TABLE_QUERIES_ADD, type: t.CUSTOMERS_TABLE_QUERIES_ADD,

View File

@@ -50,6 +50,7 @@ const ItemsCategoryList = ({
(category) => ( (category) => (
<Menu> <Menu>
<MenuItem <MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_category' })} text={formatMessage({ id: 'edit_category' })}
onClick={handelEditCategory(category)} onClick={handelEditCategory(category)}
/> />

View File

@@ -23,7 +23,11 @@ import ErrorMessage from 'components/ErrorMessage';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import MoneyInputGroup from 'components/MoneyInputGroup'; import MoneyInputGroup from 'components/MoneyInputGroup';
import Dragzone from 'components/Dragzone'; import Dragzone from 'components/Dragzone';
import { ListSelect, AccountsSelectList, If } from 'components'; import {
ListSelect,
AccountsSelectList,
CategoriesSelectList,
} from 'components';
import withItemsActions from 'containers/Items/withItemsActions'; import withItemsActions from 'containers/Items/withItemsActions';
import withItemCategories from 'containers/Items/withItemCategories'; import withItemCategories from 'containers/Items/withItemCategories';
@@ -170,7 +174,7 @@ const ItemForm = ({
}, },
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => { onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
const saveItem = (mediaIds) => { const saveItem = (mediaIds) => {
const formValues = { ...values, media_ids: mediaIds }; const formValues = { ...values };
if (itemDetail && itemDetail.id) { if (itemDetail && itemDetail.id) {
requestEditItem(itemDetail.id, formValues) requestEditItem(itemDetail.id, formValues)
.then((response) => { .then((response) => {
@@ -222,29 +226,6 @@ const ItemForm = ({
}, },
}); });
const accountItem = useCallback(
(item, { handleClick }) => (
<MenuItem
key={item.id}
text={item.name}
label={item.code}
onClick={handleClick}
/>
),
[],
);
// Filter Account Items
const filterAccounts = (query, item, _index, exactMatch) => {
const normalizedTitle = item.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return `${item.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}
};
const onItemAccountSelect = useCallback( const onItemAccountSelect = useCallback(
(filedName) => { (filedName) => {
return (account) => { return (account) => {
@@ -254,13 +235,6 @@ const ItemForm = ({
[setFieldValue], [setFieldValue],
); );
const categoryItem = useCallback(
(item, { handleClick }) => (
<MenuItem key={item.id} text={item.name} onClick={handleClick} />
),
[],
);
const requiredSpan = useMemo(() => <span class="required">*</span>, []); const requiredSpan = useMemo(() => <span class="required">*</span>, []);
const infoIcon = useMemo(() => <Icon icon="info-circle" iconSize={12} />, []); const infoIcon = useMemo(() => <Icon icon="info-circle" iconSize={12} />, []);
@@ -374,17 +348,11 @@ const ItemForm = ({
Classes.FILL, Classes.FILL,
)} )}
> >
<ListSelect <CategoriesSelectList
items={categoriesList} categoriesList={categoriesList}
noResults={<MenuItem disabled={true} text="No results." />} selecetedCategoryId={values.category_id}
itemRenderer={categoryItem} onCategorySelected={onItemAccountSelect('category_id')}
itemPredicate={filterAccounts}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
onItemSelect={onItemAccountSelect('category_id')}
selectedItem={values.customer_id}
selectedItemProp={'id'}
defaultText={<T id={'select_category'} />}
labelProp={'name'}
/> />
</FormGroup> </FormGroup>

View File

@@ -40,11 +40,8 @@ const ItemFormContainer = ({
const fetchItemDetail = useQuery( const fetchItemDetail = useQuery(
['item', id], ['item', id],
(key, _id) => requestFetchItem(_id), (key, _id) => requestFetchItem(_id),
{ { enabled: id && id },
enabled: !!id,
},
); );
const handleFormSubmit = useCallback( const handleFormSubmit = useCallback(
(payload) => { (payload) => {
payload.redirect && history.push('/items'); payload.redirect && history.push('/items');

View File

@@ -9,9 +9,7 @@ import {
Intent, Intent,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import DataTable from 'components/DataTable'; import { Icon, DataTable, Money, If, Choose } from 'components';
import Icon from 'components/Icon';
import Money from 'components/Money';
import LoadingIndicator from 'components/LoadingIndicator'; import LoadingIndicator from 'components/LoadingIndicator';
import withItems from 'containers/Items/withItems'; import withItems from 'containers/Items/withItems';
@@ -56,14 +54,19 @@ const ItemsDataTable = ({
const actionMenuList = useCallback( const actionMenuList = useCallback(
(item) => ( (item) => (
<Menu> <Menu>
<MenuItem text={formatMessage({ id: 'view_details' })} /> <MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_item' })} text={formatMessage({ id: 'edit_item' })}
onClick={handleEditItem(item)} onClick={handleEditItem(item)}
/> />
<MenuItem <MenuItem
text={formatMessage({ id: 'delete_item' })} text={formatMessage({ id: 'delete_item' })}
icon={<Icon icon="trash-16" iconSize={16} />}
onClick={handleDeleteItem(item)} onClick={handleDeleteItem(item)}
intent={Intent.DANGER} intent={Intent.DANGER}
/> />
@@ -72,9 +75,12 @@ const ItemsDataTable = ({
[handleEditItem, handleDeleteItem, formatMessage], [handleEditItem, handleDeleteItem, formatMessage],
); );
const handleRowContextMenu = useCallback((cell) => { const handleRowContextMenu = useCallback(
return actionMenuList(cell.row.original); (cell) => {
}, [actionMenuList]); return actionMenuList(cell.row.original);
},
[actionMenuList],
);
const columns = useMemo( const columns = useMemo(
() => [ () => [
@@ -155,7 +161,6 @@ const ItemsDataTable = ({
}, },
[onSelectedRowsChange], [onSelectedRowsChange],
); );
return ( return (
<LoadingIndicator loading={loading} mount={false}> <LoadingIndicator loading={loading} mount={false}>
<DataTable <DataTable

View File

@@ -211,6 +211,8 @@ export default {
once_delete_this_item_you_will_able_to_restore_it: `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?<br /><br />If you're not sure, you can inactivate it instead.`, once_delete_this_item_you_will_able_to_restore_it: `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?<br /><br />If you're not sure, you can inactivate it instead.`,
the_item_has_been_successfully_deleted: the_item_has_been_successfully_deleted:
'The item has been successfully deleted.', 'The item has been successfully deleted.',
the_item_has_been_successfully_edited:
'The item #{number} has been successfully edited.',
the_item_category_has_been_successfully_created: the_item_category_has_been_successfully_created:
'The item category has been successfully created.', 'The item category has been successfully created.',
the_item_category_has_been_successfully_edited: the_item_category_has_been_successfully_edited:

View File

@@ -1,3 +1,4 @@
import { resolve } from 'p-progress';
import ApiService from 'services/ApiService'; import ApiService from 'services/ApiService';
import t from 'store/types'; import t from 'store/types';
@@ -52,14 +53,13 @@ export const fetchCustomers = ({ query }) => {
ApiService.get(`customers`, { params: { ...pageQuery, ...query } }) ApiService.get(`customers`, { params: { ...pageQuery, ...query } })
.then((response) => { .then((response) => {
dispatch({ dispatch({
type: t.CUSTOMER_SET, type: t.CUSTOMERS_ITEMS_SET,
customers: response.data.customers, customers: response.data.customers,
}); });
dispatch({ dispatch({
type: t.CUSTOMERS_PAGE_SET, type: t.CUSTOMERS_PAGE_SET,
customers: response.data.customers, customers: response.data.customers,
customViewId: response.data.customers.customViewId, customViewId: response.data.customers?.viewMeta?.customViewId || -1,
paginationMeta: response.data.pagination, paginationMeta: response.data.pagination,
}); });
dispatch({ dispatch({
@@ -74,6 +74,26 @@ export const fetchCustomers = ({ query }) => {
}); });
}; };
export const fetchCustomer = ({ id }) => {
return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.get(`customers/${id}`)
.then((response) => {
dispatch({
type: t.CUSTOMER_SET,
payload: {
id,
customer: response.data.contact,
},
});
resolve(response);
})
.catch((error) => {
reject(error);
});
});
};
export const deleteCustomer = ({ id }) => { export const deleteCustomer = ({ id }) => {
return (dispatch) => return (dispatch) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {

View File

@@ -11,7 +11,7 @@ const initialState = {
}; };
const customersReducer = createReducer(initialState, { const customersReducer = createReducer(initialState, {
[t.CUSTOMER_SET]: (state, action) => { [t.CUSTOMERS_ITEMS_SET]: (state, action) => {
const _customers = {}; const _customers = {};
action.customers.forEach((customer) => { action.customers.forEach((customer) => {
@@ -51,6 +51,10 @@ const customersReducer = createReducer(initialState, {
}); });
state.items = items; state.items = items;
}, },
[t.CUSTOMER_SET]: (state, action) => {
const { id, customer } = action.payload;
state.items[id] = { ...customer };
},
}); });
export default createTableQueryReducers('customers', customersReducer); export default createTableQueryReducers('customers', customersReducer);

View File

@@ -27,7 +27,7 @@ export const fetchItems = ({ query }) => {
dispatch({ dispatch({
type: t.ITEMS_PAGE_SET, type: t.ITEMS_PAGE_SET,
items: response.data.items, items: response.data.items,
customViewId: response.data.customViewId, customViewId: response.data?.filter_meta?.view?.custom_view_id,
paginationMeta: response.data.pagination, paginationMeta: response.data.pagination,
}); });
dispatch({ dispatch({
@@ -55,8 +55,12 @@ export const fetchItem = ({ id }) => {
.then((response) => { .then((response) => {
dispatch({ dispatch({
type: t.ITEM_SET, type: t.ITEM_SET,
item: response.data.item, payload: {
id,
item: response.data.item,
},
}); });
resolve(response);
}) })
.catch((error) => { .catch((error) => {
reject(error); reject(error);

View File

@@ -27,6 +27,11 @@ const itemsReducer = createReducer(initialState, {
}; };
}, },
[t.ITEM_SET]: (state, action) => {
const { id, item } = action.payload;
state.items[id] = { ...item };
},
[t.ITEMS_PAGE_SET]: (state, action) => { [t.ITEMS_PAGE_SET]: (state, action) => {
const { items, customViewId, paginationMeta } = action; const { items, customViewId, paginationMeta } = action;
@@ -42,10 +47,10 @@ const itemsReducer = createReducer(initialState, {
state.itemsRelation[item.id] = []; state.itemsRelation[item.id] = [];
} }
const filteredRelation = state.itemsRelation[item.id].filter( const filteredRelation = state.itemsRelation[item.id].filter(
(relation) => ( (relation) =>
relation.viewId === viewId && relation.viewId === viewId &&
relation.pageNumber === paginationMeta.page relation.pageNumber === paginationMeta.page,
)); );
filteredRelation.push({ filteredRelation.push({
viewId, viewId,

View File

@@ -1,7 +1,6 @@
export default { export default {
ITEMS_SET: 'ITEMS_SET', ITEMS_SET: 'ITEMS_SET',
ITEM_SET: 'ITEM_SET',
ITEMS_PAGE_SET: 'ITEMS_PAGE_SET', ITEMS_PAGE_SET: 'ITEMS_PAGE_SET',
ITEM_DELETE: 'ITEM_DELETE', ITEM_DELETE: 'ITEM_DELETE',
ITEM_BULK_ACTION_ADD: 'ITEM_BULK_ACTION_ADD', ITEM_BULK_ACTION_ADD: 'ITEM_BULK_ACTION_ADD',
@@ -12,6 +11,5 @@ export default {
ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING', ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING',
ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW', ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW',
ITEMS_BULK_DELETE:'ITEMS_BULK_DELETE' ITEMS_BULK_DELETE: 'ITEMS_BULK_DELETE',
}; };