@krowdy-ui/views
Version:
React components that implement Google's Material Design.
541 lines (517 loc) • 18.5 kB
JavaScript
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import _extends from "@babel/runtime/helpers/esm/extends";
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import AddIcon from '@material-ui/icons/Add';
import { IconButton, Divider } from '@material-ui/core';
import ArrowBackIcon from '@material-ui/icons/ArrowBackIos';
import { Button, Card, CardContent, CardHeader, TabPanel, Typography, withStyles } from '@krowdy-ui/core';
import FiltersTree from './FiltersTree';
import FilterConfig from './FilterConfig';
import FiltersList from './FiltersList';
import DividerWithText from './DividerWithText';
import { mongoObjectId } from '../utils/mongoObjectId';
import { AddToPhotos as AddToPhotosIcon, Delete as DeleteIcon, Face as FaceIcon } from '@material-ui/icons';
import { generateRandomId } from '../utils';
import KeywordFilter from './KeywordFilter';
export const styles = theme => ({
backIcon: {
marginRight: theme.spacing(1),
paddingLeft: theme.spacing(.75),
paddingRight: 0
},
block: {
width: 18
},
buttonAddContainer: {
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
marginTop: theme.spacing(1)
},
buttonAddGroupFilter: {
borderStyle: 'dashed',
margin: theme.spacing(1.5, 0)
},
cardContent: {
display: 'flex',
flex: 2,
justifyContent: 'center',
overflowY: 'auto'
},
cardTitle: {
fontSize: 14
},
center: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column'
},
flex: {
alignItems: 'center',
display: 'flex'
},
groupFilterContainer: {
'&:hover': {
backgroundColor: theme.palette.primary[50],
border: `1px solid ${theme.palette.primary[500]}`
},
backgroundColor: theme.palette.secondary[10],
border: `1px solid ${theme.palette.secondary[10]}`,
borderRadius: 2 * theme.shape.borderRadius,
overflowX: 'auto',
padding: theme.spacing(0.5)
},
groupFilterContainerBlock: {
'&:hover': {
backgroundColor: theme.palette.secondary[100],
border: `1px solid ${theme.palette.secondary[200]}`
},
backgroundColor: theme.palette.secondary[100],
border: `1px solid ${theme.palette.secondary[100]}`,
borderRadius: theme.shape.borderRadius,
overflowX: 'auto',
padding: theme.spacing(0.5)
},
littleMarginLeft: {
marginLeft: theme.spacing(.5)
},
marginTop: {
marginTop: theme.spacing(1.5)
},
noPadding: {
padding: 0
},
root: {
display: 'flex',
flexFlow: 'column',
height: '100%'
},
spaceBetween: {
justifyContent: 'space-between',
width: '100%'
},
titleBack: {
fontSize: 14
},
titleContainer: {
alignItems: 'center',
display: 'flex',
height: 20
},
treeContainer: {
'& *, & *::after, & *::before': {
boxSizing: 'content-box'
},
overflowY: 'scroll'
},
viewContainer: {
height: '100%',
width: '100%'
}
});
const Views = {
FILTERS_SEARCH: {
backIndex: 'HOME',
index: 1,
withPadding: false
},
FILTER_CONFIG: {
backIndex: 'FILTERS_SEARCH',
index: 2,
withPadding: true
},
HOME: {
index: 0,
withPadding: true
}
};
const CandidateGroupFilterType = {
Excluded: 'excluded',
Included: 'included'
};
var _ref2 = /*#__PURE__*/React.createElement(ArrowBackIcon, {
fontSize: "inherit"
});
var _ref3 = /*#__PURE__*/React.createElement(AddIcon, null);
var _ref4 = /*#__PURE__*/React.createElement(FaceIcon, {
color: "disabled"
});
var _ref5 = /*#__PURE__*/React.createElement(DividerWithText, {
title: 'or'
});
var _ref6 = /*#__PURE__*/React.createElement(AddIcon, null);
var _ref7 = /*#__PURE__*/React.createElement(DeleteIcon, {
fontSize: "small"
});
var _ref8 = /*#__PURE__*/React.createElement(DividerWithText, {
title: 'or'
});
const SuperFilters = props => {
const {
classes,
listRef,
title = 'Todos las compras',
headerHomeComponent: HeaderHomeComponent = null,
filters = [],
onChangeFilters = () => {},
onSelectCategoryFilter,
categoryItems = [],
hasNextPage = false,
filterTypes = [],
loadMoreCategoryItems = () => {},
isLoadingMoreCategoryItems = false,
listWidth,
filterGroups = [],
dots = false,
uniqueFilter = false,
totalItems,
onChangeFilterCandidate = () => {},
excludedCandidates = [],
childProps = {},
includedCandidates = [],
viewDefault,
shortcuts
} = props;
const {
PaperProps,
AddFiltersButtonProps
} = childProps;
const [groupFilterCurrent, setGroupFilterCurrent] = useState(null);
const [view, goToView] = useState(Views[viewDefault] || Views.HOME);
const [filterSelected, setFilterSelected] = useState();
const [filterToEdit, setFilterToEdit] = useState();
const [openKeywordFilter, setOpenKeywordFilter] = useState(false);
const {
groupFilterCurrentKey,
groupFilterCurrentChildren
} = useMemo(() => ({
groupFilterCurrentChildren: groupFilterCurrent ? groupFilterCurrent.children : null,
groupFilterCurrentKey: groupFilterCurrent ? groupFilterCurrent.key : null
}), [groupFilterCurrent]);
const addFilter = useCallback(filter => {
onChangeFilters(filters.map(groupFilter => {
if (groupFilter.key !== groupFilterCurrentKey) return groupFilter;
return _extends({}, groupFilter, {
children: groupFilter.children.concat(filter)
});
}));
}, [filters, groupFilterCurrentKey, onChangeFilters]);
const deepUpdate = useCallback((arr, _ref) => {
let {
_id
} = _ref,
updatedItem = _objectWithoutPropertiesLoose(_ref, ["_id"]);
return arr.map(item => {
if (item._id === _id) return _extends({}, item, updatedItem);
if (Array.isArray(item.children) && item.children.length) return _extends({}, item, {
children: deepUpdate(item.children, _extends({
_id
}, updatedItem))
});
return item;
});
}, []);
const updateFilter = useCallback(filter => {
const updatedFilters = deepUpdate(groupFilterCurrentChildren, filter);
onChangeFilters(filters.map(groupFilter => {
if (groupFilter.key !== groupFilterCurrentKey) return groupFilter;
return _extends({}, groupFilter, {
children: updatedFilters
});
}));
}, [deepUpdate, filters, groupFilterCurrentChildren, groupFilterCurrentKey, onChangeFilters]); // Aqui es cuando se agrega un filtro
const _handleClickApplyFilters = useCallback(filter => {
if (Object.values(CandidateGroupFilterType).includes(groupFilterCurrentKey)) onChangeFilterCandidate(groupFilterCurrentKey, filter.value);else if (filters.some(({
key
}) => key === groupFilterCurrentKey)) {
if (filterToEdit) {
// Exists, so update it
updateFilter(filter);
setFilterToEdit(null);
} else {
addFilter(filter);
}
} else onChangeFilters(filters.concat({
children: [filter],
key: mongoObjectId(),
operator: 'none',
type: 'default'
}));
goToView(Views.HOME);
setGroupFilterCurrent(null);
setOpenKeywordFilter(false);
}, [addFilter, filterToEdit, filters, groupFilterCurrentKey, onChangeFilterCandidate, onChangeFilters, updateFilter]);
const _handleClickFilterListItem = useCallback(item => {
if (item.type === 'shortcut') {
setFilterSelected(item.treeData);
const [tree] = item.treeData;
_handleClickApplyFilters(tree);
} else {
setFilterSelected(item);
if (item.type === 'keyword') setOpenKeywordFilter(true);else goToView(Views.FILTER_CONFIG);
}
}, [_handleClickApplyFilters]);
const _handleClickAddFilter = useCallback(groupFilter => () => {
setGroupFilterCurrent(groupFilter);
goToView(Views.FILTERS_SEARCH);
}, []);
const _handleClickAddGroupFilter = useCallback(() => {
setGroupFilterCurrent({
children: [],
key: mongoObjectId(),
operator: 'none',
type: 'default'
});
goToView(Views.FILTERS_SEARCH);
}, []);
const _handleClickDeleteGroupFilter = useCallback(groupFilterKey => () => {
onChangeFilters(filters.filter(groupFilter => groupFilter.key !== groupFilterKey));
}, [filters, onChangeFilters]);
const _handleClickBack = useCallback(() => {
if ([CandidateGroupFilterType.Included, CandidateGroupFilterType.Excluded].includes(groupFilterCurrentKey)) {
goToView(Views.HOME);
setFilterToEdit(null);
setGroupFilterCurrent(null);
return;
}
goToView(Views[view.backIndex]);
setFilterToEdit(null);
if (view.backIndex === 'HOME') setGroupFilterCurrent(null);
}, [groupFilterCurrentKey, view.backIndex]);
const _handleChangeFilterTree = useCallback(groupFilterKey => treeFilters => {
onChangeFilters(filters.map(groupFilter => {
if (groupFilterKey !== groupFilter.key) return groupFilter;
return _extends({}, groupFilter, {
children: treeFilters
});
}).filter(({
children
}) => children.length));
}, [filters, onChangeFilters]);
const _handleClickEditFilter = useCallback(groupFilter => appliedFilter => {
setGroupFilterCurrent(groupFilter);
setFilterToEdit(appliedFilter);
if (appliedFilter.type === 'keyword') setOpenKeywordFilter(true);else goToView(Views.FILTER_CONFIG);
}, []);
const _handleChangeFilterCandidates = useCallback(candidateGroupFilterType => () => {
onChangeFilterCandidate(candidateGroupFilterType, []);
}, [onChangeFilterCandidate]);
const _handleClickEditFilterCandidates = useCallback(candidateGroupFilterType => treeFilters => {
setGroupFilterCurrent({
key: candidateGroupFilterType
});
setFilterToEdit(treeFilters);
goToView(Views.FILTER_CONFIG);
}, []);
const _handleCloseKeywordFilter = useCallback(() => {
setOpenKeywordFilter(false);
}, []);
return /*#__PURE__*/React.createElement(Card, {
className: classes.root,
variant: "outlined"
}, /*#__PURE__*/React.createElement(CardHeader, {
shadow: true,
title: /*#__PURE__*/React.createElement("div", {
className: classes.titleContainer
}, view.index !== 0 && /*#__PURE__*/React.createElement(IconButton, {
"aria-label": "delete",
className: classes.backIcon,
onClick: _handleClickBack,
size: "small"
}, _ref2), /*#__PURE__*/React.createElement("p", {
className: classes.titleBack
}, view.index === 0 ? title : 'Atrás'))
}), /*#__PURE__*/React.createElement(CardContent, {
className: clsx(classes.cardContent, !view.withPadding && classes.noPadding)
}, /*#__PURE__*/React.createElement(TabPanel, {
className: classes.viewContainer,
index: Views.HOME.index,
value: view.index
}, filters.length === 0 ? HeaderHomeComponent : null, !filters.length && !includedCandidates.length && !excludedCandidates.length ? /*#__PURE__*/React.createElement(Button, {
color: "primary",
fullWidth: true,
onClick: _handleClickAddGroupFilter,
startIcon: _ref3
}, "A\xF1adir Filtro") : /*#__PURE__*/React.createElement("div", {
className: clsx(classes.flex, classes.spaceBetween)
}, /*#__PURE__*/React.createElement(IconButton, {
color: "primary",
size: "small"
}, /*#__PURE__*/React.createElement(AddToPhotosIcon, {
fontSize: "small",
onClick: _handleClickAddGroupFilter
})), !isNaN(totalItems) ? /*#__PURE__*/React.createElement("div", {
className: classes.flex
}, _ref4, /*#__PURE__*/React.createElement(Typography, {
className: classes.littleMarginLeft,
variant: "body1"
}, totalItems, " resultado", totalItems > 1 ? 's' : '')) : null), /*#__PURE__*/React.createElement("div", {
className: clsx(classes.marginTop, classes.treeContainer)
}, excludedCandidates.length ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: classes.groupFilterContainerBlock
}, /*#__PURE__*/React.createElement(FiltersTree, {
dots: dots,
onChange: _handleChangeFilterCandidates(CandidateGroupFilterType.Excluded),
onClickEdit: _handleClickEditFilterCandidates(CandidateGroupFilterType.Excluded),
treeData: [{
_id: generateRandomId(),
key: '_id',
label: 'Candidato',
operator: '$nin',
operatorLabel: 'no es',
optionIndex: 1,
reference: null,
type: 'list',
value: excludedCandidates
}]
})), includedCandidates.length || filters.length ? /*#__PURE__*/React.createElement(Divider, {
className: classes.marginTop
}) : null) : null, includedCandidates.length ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: clsx(classes.marginTop, classes.groupFilterContainer)
}, /*#__PURE__*/React.createElement(FiltersTree, {
dots: dots,
onChange: _handleChangeFilterCandidates(CandidateGroupFilterType.Included),
onClickEdit: _handleClickEditFilterCandidates(CandidateGroupFilterType.Included),
treeData: [{
_id: generateRandomId(),
key: '_id',
label: 'Candidato',
operator: '$in',
operatorLabel: 'es',
optionIndex: 0,
reference: null,
type: 'list',
value: includedCandidates
}]
})), filters.length ? _ref5 : null) : null, filters.map((groupFilter, index) => /*#__PURE__*/React.createElement("div", {
key: `GroupFilterDefault-${index}`
}, /*#__PURE__*/React.createElement("div", {
className: classes.groupFilterContainer
}, /*#__PURE__*/React.createElement(FiltersTree, {
dots: dots,
onChange: _handleChangeFilterTree(groupFilter.key),
onClickEdit: _handleClickEditFilter(groupFilter),
treeData: groupFilter.children
}), /*#__PURE__*/React.createElement("div", {
className: classes.buttonAddContainer
}, /*#__PURE__*/React.createElement("div", {
className: classes.block
}), /*#__PURE__*/React.createElement(Button, {
color: "primary",
onClick: _handleClickAddFilter(groupFilter),
startIcon: _ref6
}, "Filtro"), groupFilter.children.length ? /*#__PURE__*/React.createElement("div", {
className: classes.block
}) : /*#__PURE__*/React.createElement(IconButton, {
color: "primary",
onClick: _handleClickDeleteGroupFilter(groupFilter.key),
size: "small"
}, _ref7))), index + 1 < filters.length && _ref8)))), /*#__PURE__*/React.createElement(TabPanel, {
className: classes.viewContainer,
index: Views.FILTERS_SEARCH.index,
value: view.index
}, /*#__PURE__*/React.createElement(FiltersList, {
filterGroups: shortcuts && shortcuts.length ? filterGroups.concat(shortcuts) : filterGroups,
filters: groupFilterCurrent ? groupFilterCurrent.children : [],
listRef: listRef,
onClickItem: _handleClickFilterListItem,
uniqueFilter: uniqueFilter
})), /*#__PURE__*/React.createElement(TabPanel, {
className: classes.viewContainer,
index: Views.FILTER_CONFIG.index,
value: view.index
}, /*#__PURE__*/React.createElement(FilterConfig, {
AddFiltersButtonProps: AddFiltersButtonProps,
categoryItems: categoryItems,
edit: filterToEdit ? true : false,
filter: filterToEdit || filterSelected,
filterTypes: filterTypes,
hasNextPage: hasNextPage,
listWidth: listWidth,
loadMoreCategoryItems: loadMoreCategoryItems,
onClickApply: _handleClickApplyFilters,
onSelectCategoryFilter: onSelectCategoryFilter,
PaperProps: PaperProps
})), /*#__PURE__*/React.createElement(KeywordFilter, {
edit: !!filterToEdit,
filter: filterToEdit || filterSelected,
isLoading: isLoadingMoreCategoryItems,
isOpen: openKeywordFilter,
items: categoryItems,
loadMore: loadMoreCategoryItems,
onClickApply: _handleClickApplyFilters,
onClose: _handleCloseKeywordFilter,
onResetCategoryItems: onSelectCategoryFilter,
PaperProps: PaperProps
})));
};
process.env.NODE_ENV !== "production" ? SuperFilters.propTypes = {
categoryItems: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired
})).isRequired,
childProps: PropTypes.object,
classes: PropTypes.object,
excludedCandidates: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
label: PropTypes.string
})),
filterGroups: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string.isRequired,
children: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string.isRequired,
key: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
type: PropTypes.string.isRequired
})),
label: PropTypes.string.isRequired
})),
filterTypes: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string.isRequired,
initialValue: PropTypes.any.isRequired,
options: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
numberOfInputs: PropTypes.number.isRequired,
operator: PropTypes.string.isRequired
})).isRequired,
type: PropTypes.string.isRequired
})).isRequired,
filters: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string,
key: PropTypes.string.isRequired,
label: PropTypes.string,
operator: PropTypes.string.isRequired,
operatorLabel: PropTypes.string,
optionIndex: PropTypes.number,
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array])
})),
hasNextPage: PropTypes.bool,
headerHomeComponent: PropTypes.node,
includedCandidates: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
label: PropTypes.string
})),
isLoadingMoreCategoryItems: PropTypes.bool,
listWidth: PropTypes.number,
loadMoreCategoryItems: PropTypes.func,
onChangeFilterCandidate: PropTypes.func,
onChangeFilters: PropTypes.func.isRequired,
onFetchFilterGroups: PropTypes.func,
onSelectCategoryFilter: PropTypes.func,
shortcuts: PropTypes.arrayOf(PropTypes.shape({
_id: PropTypes.string,
children: PropTypes.array,
label: PropTypes.string,
type: PropTypes.string.isRequired
})),
title: PropTypes.string.isRequired,
uniqueFilter: PropTypes.bool,
viewDefault: PropTypes.oneOf(['HOME', 'FILTERS_SEARCH', 'FILTER_CONFIG'])
} : void 0;
export default withStyles(styles, {
name: 'SuperFilters'
})(SuperFilters);