diff --git a/src/actions/item.js b/src/actions/item.js
index d059dba73..0809a274d 100644
--- a/src/actions/item.js
+++ b/src/actions/item.js
@@ -22,6 +22,8 @@ import {
FLAG_SETTING_ITEM,
FLAG_EDITING_ITEM,
GET_SHARED_ITEMS_SUCCESS,
+ FLAG_DELETING_ITEMS,
+ DELETE_ITEMS_SUCCESS,
} from '../types/item';
import { getParentsIdsFromPath } from '../utils/item';
import { createFlag } from './utils';
@@ -129,13 +131,35 @@ export const createItem = (props) => async (dispatch) => {
}
};
-export const deleteItem = (item) => async (dispatch) => {
+export const deleteItem = (itemId) => async (dispatch) => {
try {
dispatch(createFlag(FLAG_DELETING_ITEM, true));
- await Api.deleteItem(item.id);
+ await Api.deleteItem(itemId);
dispatch({
type: DELETE_ITEM_SUCCESS,
- payload: item,
+ payload: { id: itemId },
+ });
+ } catch (e) {
+ console.error(e);
+ } finally {
+ dispatch(createFlag(FLAG_DELETING_ITEM, false));
+ }
+};
+
+export const deleteItems = (itemIds) => async (dispatch) => {
+ try {
+ dispatch(createFlag(FLAG_DELETING_ITEMS, true));
+
+ // choose corresponding call depending on number of items
+ if (itemIds.length === 1) {
+ await Api.deleteItem(itemIds);
+ } else {
+ await Api.deleteItems(itemIds);
+ }
+
+ dispatch({
+ type: DELETE_ITEMS_SUCCESS,
+ payload: itemIds,
});
} catch (e) {
console.error(e);
diff --git a/src/api/item.js b/src/api/item.js
index 72ef9edb6..71287df0a 100644
--- a/src/api/item.js
+++ b/src/api/item.js
@@ -2,6 +2,7 @@ import { API_HOST, ROOT_ID } from '../config/constants';
import {
buildCopyItemRoute,
buildDeleteItemRoute,
+ buildDeleteItemsRoute,
buildEditItemRoute,
buildGetChildrenRoute,
buildGetItemRoute,
@@ -87,6 +88,20 @@ export const deleteItem = async (id) => {
return res.json();
};
+export const deleteItems = async (ids) => {
+ const res = await fetch(
+ `${API_HOST}/${buildDeleteItemsRoute(ids)}`,
+ DEFAULT_DELETE,
+ );
+
+ if (!res.ok) {
+ throw new Error((await res.json()).message);
+ }
+ await CacheOperations.deleteItems(ids);
+
+ return res.json();
+};
+
// payload = {name, type, description, extra}
// querystring = {parentId}
export const editItem = async (item) => {
diff --git a/src/api/routes.js b/src/api/routes.js
index ea7ec8d5b..1959c24e7 100644
--- a/src/api/routes.js
+++ b/src/api/routes.js
@@ -8,6 +8,8 @@ export const buildPostItemRoute = (parentId) => {
return url;
};
export const buildDeleteItemRoute = (id) => `items/${id}`;
+export const buildDeleteItemsRoute = (ids) =>
+ `items?${ids.map((id) => `id=${id}`).join('&')}`;
export const buildGetChildrenRoute = (id) => `items/${id}/children`;
export const buildGetItemRoute = (id) => `items/${id}`;
export const buildMoveItemRoute = (id) => `items/${id}/move`;
diff --git a/src/components/Root.js b/src/components/Root.js
index 298660e51..fa2bfc616 100644
--- a/src/components/Root.js
+++ b/src/components/Root.js
@@ -11,6 +11,7 @@ const theme = createMuiTheme({
primary: {
main: '#5050d2',
},
+ secondary: { main: '#ffffff' },
},
});
diff --git a/src/components/common/DeleteButton.js b/src/components/common/DeleteButton.js
new file mode 100644
index 000000000..a0991871e
--- /dev/null
+++ b/src/components/common/DeleteButton.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { useTranslation } from 'react-i18next';
+import Tooltip from '@material-ui/core/Tooltip';
+import PropTypes from 'prop-types';
+import IconButton from '@material-ui/core/IconButton';
+import DeleteIcon from '@material-ui/icons/Delete';
+import { deleteItems } from '../../actions/item';
+import { ITEM_DELETE_BUTTON_CLASS } from '../../config/selectors';
+
+const DeleteButton = ({ itemIds, dispatchDeleteItems, color }) => {
+ const { t } = useTranslation();
+
+ return (
+
+ dispatchDeleteItems(itemIds)}
+ >
+
+
+
+ );
+};
+
+DeleteButton.propTypes = {
+ itemIds: PropTypes.string.isRequired,
+ dispatchDeleteItems: PropTypes.func.isRequired,
+ color: PropTypes.string,
+};
+
+DeleteButton.defaultProps = {
+ color: '',
+};
+
+const mapDispatchToProps = {
+ dispatchDeleteItems: deleteItems,
+};
+
+const ConnectedComponent = connect(null, mapDispatchToProps)(DeleteButton);
+
+export default ConnectedComponent;
diff --git a/src/components/common/EditButton.js b/src/components/common/EditButton.js
new file mode 100644
index 000000000..71dc89bd9
--- /dev/null
+++ b/src/components/common/EditButton.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import IconButton from '@material-ui/core/IconButton';
+import EditIcon from '@material-ui/icons/Edit';
+import { useTranslation } from 'react-i18next';
+import Tooltip from '@material-ui/core/Tooltip';
+import { ITEM_MENU_EDIT_BUTTON_CLASS } from '../../config/selectors';
+import { setEditModalSettings } from '../../actions/layout';
+
+const Item = ({ itemId, dispatchSetEditModalSettings }) => {
+ const { t } = useTranslation();
+
+ const handleEdit = () => {
+ dispatchSetEditModalSettings({ open: true, itemId });
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+Item.propTypes = {
+ itemId: PropTypes.string.isRequired,
+ dispatchSetEditModalSettings: PropTypes.func.isRequired,
+};
+
+const mapDispatchToProps = {
+ dispatchSetEditModalSettings: setEditModalSettings,
+};
+
+const ConnectedComponent = connect(null, mapDispatchToProps)(Item);
+
+export default ConnectedComponent;
diff --git a/src/components/common/ShareButton.js b/src/components/common/ShareButton.js
new file mode 100644
index 000000000..b91e6c03c
--- /dev/null
+++ b/src/components/common/ShareButton.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import IconButton from '@material-ui/core/IconButton';
+import ShareIcon from '@material-ui/icons/Share';
+import { useTranslation } from 'react-i18next';
+import Tooltip from '@material-ui/core/Tooltip';
+import { ITEM_MENU_SHARE_BUTTON_CLASS } from '../../config/selectors';
+import { setShareModalSettings } from '../../actions/layout';
+
+const Item = ({ itemId, dispatchSetShareModalSettings }) => {
+ const { t } = useTranslation();
+
+ const handleShare = () => {
+ dispatchSetShareModalSettings({ open: true, itemId });
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+Item.propTypes = {
+ itemId: PropTypes.string.isRequired,
+ dispatchSetShareModalSettings: PropTypes.func.isRequired,
+};
+
+const mapDispatchToProps = {
+ dispatchSetShareModalSettings: setShareModalSettings,
+};
+
+const ConnectedComponent = connect(null, mapDispatchToProps)(Item);
+
+export default ConnectedComponent;
diff --git a/src/components/main/CustomCardHeader.js b/src/components/main/CustomCardHeader.js
index c2210aa2e..82baced4a 100644
--- a/src/components/main/CustomCardHeader.js
+++ b/src/components/main/CustomCardHeader.js
@@ -59,7 +59,7 @@ const CustomCardHeader = ({ item }) => {
-
+
);
};
diff --git a/src/components/main/EmptyItem.js b/src/components/main/EmptyItem.js
new file mode 100644
index 000000000..7e761266c
--- /dev/null
+++ b/src/components/main/EmptyItem.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import Typography from '@material-ui/core/Typography';
+import { useTranslation } from 'react-i18next';
+import { ITEMS_GRID_NO_ITEM_ID } from '../../config/selectors';
+
+const EmptyItem = () => {
+ const { t } = useTranslation();
+
+ return (
+
+ {t('This item is empty.')}
+
+ );
+};
+
+export default EmptyItem;
diff --git a/src/components/main/Home.js b/src/components/main/Home.js
index f40b8c260..946e6060d 100644
--- a/src/components/main/Home.js
+++ b/src/components/main/Home.js
@@ -1,4 +1,5 @@
import React, { Component } from 'react';
+import { List } from 'immutable';
import PropTypes from 'prop-types';
import Divider from '@material-ui/core/Divider';
import { connect } from 'react-redux';
@@ -15,8 +16,8 @@ class Home extends Component {
params: PropTypes.shape({ itemId: PropTypes.string }).isRequired,
}).isRequired,
activity: PropTypes.bool.isRequired,
- ownItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
- sharedItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ ownItems: PropTypes.instanceOf(List).isRequired,
+ sharedItems: PropTypes.instanceOf(List).isRequired,
t: PropTypes.func.isRequired,
dispatchGetSharedItems: PropTypes.func.isRequired,
};
@@ -62,7 +63,7 @@ class Home extends Component {
}
const mapStateToProps = ({ item }) => ({
- activity: Object.values(item.get('activity').toJS()).flat().length,
+ activity: Boolean(Object.values(item.get('activity').toJS()).flat().length),
ownItems: item.get('own'),
sharedItems: item.get('shared'),
});
diff --git a/src/components/main/Item.js b/src/components/main/Item.js
index d3cd6470e..95770808e 100644
--- a/src/components/main/Item.js
+++ b/src/components/main/Item.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import truncate from 'lodash.truncate';
@@ -7,19 +6,16 @@ import Card from '@material-ui/core/Card';
import CardMedia from '@material-ui/core/CardMedia';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
-import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
-import DeleteIcon from '@material-ui/icons/Delete';
import CustomCardHeader from './CustomCardHeader';
-import { deleteItem } from '../../actions/item';
import {
DEFAULT_IMAGE_SRC,
DESCRIPTION_MAX_LENGTH,
} from '../../config/constants';
-import {
- buildItemCard,
- ITEM_DELETE_BUTTON_CLASS,
-} from '../../config/selectors';
+import { buildItemCard } from '../../config/selectors';
+import EditButton from '../common/EditButton';
+import ShareButton from '../common/ShareButton';
+import DeleteButton from '../common/DeleteButton';
const useStyles = makeStyles(() => ({
root: {
@@ -31,7 +27,7 @@ const useStyles = makeStyles(() => ({
},
}));
-const Item = ({ item, dispatchDeleteItem }) => {
+const Item = ({ item }) => {
const classes = useStyles();
const { id, name, description, extra } = item;
@@ -49,13 +45,9 @@ const Item = ({ item, dispatchDeleteItem }) => {
- dispatchDeleteItem(item)}
- >
-
-
+
+
+
);
@@ -72,13 +64,6 @@ Item.propTypes = {
image: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
- dispatchDeleteItem: PropTypes.func.isRequired,
-};
-
-const mapDispatchToProps = {
- dispatchDeleteItem: deleteItem,
};
-const ConnectedComponent = connect(null, mapDispatchToProps)(Item);
-
-export default ConnectedComponent;
+export default Item;
diff --git a/src/components/main/ItemMenu.js b/src/components/main/ItemMenu.js
index a47f7923c..5ebddd725 100644
--- a/src/components/main/ItemMenu.js
+++ b/src/components/main/ItemMenu.js
@@ -9,25 +9,19 @@ import MoreVertIcon from '@material-ui/icons/MoreVert';
import {
setMoveModalSettings,
setCopyModalSettings,
- setEditModalSettings,
- setShareModalSettings,
} from '../../actions/layout';
import {
buildItemMenu,
ITEM_MENU_BUTTON_CLASS,
ITEM_MENU_COPY_BUTTON_CLASS,
- ITEM_MENU_EDIT_BUTTON_CLASS,
ITEM_MENU_MOVE_BUTTON_CLASS,
- ITEM_MENU_SHARE_BUTTON_CLASS,
} from '../../config/selectors';
import { editItem } from '../../actions/item';
const ItemMenu = ({
- item,
+ itemId,
dispatchSetMoveModalSettings,
dispatchSetCopyModalSettings,
- dispatchSetEditModalSettings,
- dispatchSetShareModalSettings,
}) => {
const [anchorEl, setAnchorEl] = React.useState(null);
const { t } = useTranslation();
@@ -41,22 +35,12 @@ const ItemMenu = ({
};
const handleMove = () => {
- dispatchSetMoveModalSettings({ open: true, itemId: item.id });
+ dispatchSetMoveModalSettings({ open: true, itemId });
handleClose();
};
const handleCopy = () => {
- dispatchSetCopyModalSettings({ open: true, itemId: item.id });
- handleClose();
- };
-
- const handleEdit = () => {
- dispatchSetEditModalSettings({ open: true, itemId: item.id });
- handleClose();
- };
-
- const handleShare = () => {
- dispatchSetShareModalSettings({ open: true, itemId: item.id });
+ dispatchSetCopyModalSettings({ open: true, itemId });
handleClose();
};
@@ -66,47 +50,32 @@ const ItemMenu = ({
>
);
};
ItemMenu.propTypes = {
- dispatchSetEditModalSettings: PropTypes.func.isRequired,
- item: PropTypes.shape({
- id: PropTypes.string.isRequired,
- }).isRequired,
+ itemId: PropTypes.string.isRequired,
dispatchSetMoveModalSettings: PropTypes.func.isRequired,
dispatchSetCopyModalSettings: PropTypes.func.isRequired,
- dispatchSetShareModalSettings: PropTypes.func.isRequired,
};
const mapDispatchToProps = {
dispatchSetMoveModalSettings: setMoveModalSettings,
dispatchSetCopyModalSettings: setCopyModalSettings,
- dispatchSetEditModalSettings: setEditModalSettings,
- dispatchSetShareModalSettings: setShareModalSettings,
dispatchEditItem: editItem,
};
diff --git a/src/components/main/Items.js b/src/components/main/Items.js
index fbfe61a5a..6f9a69e0f 100644
--- a/src/components/main/Items.js
+++ b/src/components/main/Items.js
@@ -1,27 +1,17 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next';
-import Typography from '@material-ui/core/Typography';
+import { List } from 'immutable';
import { withRouter } from 'react-router';
-import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import { MODES } from '../../config/constants';
import ItemsTable from './ItemsTable';
-import NewItemButton from './NewItemButton';
import ItemsGrid from './ItemsGrid';
-const styles = (theme) => ({
- title: {
- display: 'flex',
- alignItems: 'center',
- marginBottom: theme.spacing(1),
- },
-});
-
// eslint-disable-next-line react/prefer-stateless-function
class Items extends Component {
static propTypes = {
- items: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ items: PropTypes.instanceOf(List).isRequired,
mode: PropTypes.oneOf(Object.values(MODES)).isRequired,
title: PropTypes.string.isRequired,
classes: PropTypes.shape({
@@ -30,15 +20,9 @@ class Items extends Component {
};
render() {
- const { items, mode, title, classes } = this.props;
+ const { items, mode, title } = this.props;
return mode === MODES.CARD ? (
- <>
-
- {title}
-
-
-
- >
+
) : (
);
@@ -50,6 +34,5 @@ const mapStateToProps = ({ layout }) => ({
});
const ConnectedComponent = connect(mapStateToProps)(Items);
-const StyledComponent = withStyles(styles)(ConnectedComponent);
-const TranslatedComponent = withTranslation()(StyledComponent);
+const TranslatedComponent = withTranslation()(ConnectedComponent);
export default withRouter(TranslatedComponent);
diff --git a/src/components/main/ItemsGrid.js b/src/components/main/ItemsGrid.js
index 713306d9d..b4cffc426 100644
--- a/src/components/main/ItemsGrid.js
+++ b/src/components/main/ItemsGrid.js
@@ -2,35 +2,37 @@ import React, { Component } from 'react';
import { List } from 'immutable';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
-import { withTranslation } from 'react-i18next';
-import Grid from '@material-ui/core/Grid';
+import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
+import Grid from '@material-ui/core/Grid';
+import NewItemButton from './NewItemButton';
import Item from './Item';
-import { ITEMS_GRID_NO_ITEM_ID } from '../../config/selectors';
+import EmptyItem from './EmptyItem';
+const styles = (theme) => ({
+ title: {
+ display: 'flex',
+ alignItems: 'center',
+ marginBottom: theme.spacing(1),
+ },
+});
class ItemsGrid extends Component {
static propTypes = {
items: PropTypes.instanceOf(List).isRequired,
match: PropTypes.shape({
params: PropTypes.shape({ itemId: PropTypes.string }).isRequired,
}).isRequired,
- t: PropTypes.func.isRequired,
+ classes: PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ }).isRequired,
+ title: PropTypes.string.isRequired,
};
renderItems = () => {
- const { items, t } = this.props;
+ const { items } = this.props;
if (!items || !items.size) {
- return (
-
- {t('No Item Here')}
-
- );
+ return ;
}
return items.map((item) => (
@@ -41,8 +43,13 @@ class ItemsGrid extends Component {
};
render() {
+ const { classes, title } = this.props;
return (
<>
+
+ {title}
+
+
{this.renderItems()}
@@ -50,5 +57,5 @@ class ItemsGrid extends Component {
);
}
}
-const TranslatedComponent = withTranslation()(ItemsGrid);
-export default withRouter(TranslatedComponent);
+const StyledComponent = withStyles(styles)(ItemsGrid);
+export default withRouter(StyledComponent);
diff --git a/src/components/main/ItemsTable.js b/src/components/main/ItemsTable.js
index 3d5237af4..73160e417 100644
--- a/src/components/main/ItemsTable.js
+++ b/src/components/main/ItemsTable.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { List } from 'immutable';
import clsx from 'clsx';
@@ -17,18 +17,20 @@ import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import Checkbox from '@material-ui/core/Checkbox';
-import IconButton from '@material-ui/core/IconButton';
-import Tooltip from '@material-ui/core/Tooltip';
-import DeleteIcon from '@material-ui/icons/Delete';
+import ItemMenu from './ItemMenu';
import { buildItemPath } from '../../config/paths';
import {
ORDERING,
TABLE_MIN_WIDTH,
ROWS_PER_PAGE_OPTIONS,
+ ITEM_DATA_TYPES,
} from '../../config/constants';
-import { getComparator, stableSort } from '../../utils/table';
+import { getComparator, stableSort, getRowsForPage } from '../../utils/table';
import { formatDate } from '../../utils/date';
import NewItemButton from './NewItemButton';
+import EditButton from '../common/EditButton';
+import ShareButton from '../common/ShareButton';
+import DeleteButton from '../common/DeleteButton';
const EnhancedTableHead = (props) => {
const {
@@ -61,8 +63,7 @@ const EnhancedTableHead = (props) => {
{headCells.map((headCell) => (
({
display: 'flex',
alignItems: 'center',
},
+ highlight: {
+ background: theme.palette.primary.main,
+ color: 'white',
+ },
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
const { t } = useTranslation();
- const { numSelected, tableTitle } = props;
+ const { numSelected, tableTitle, selected } = props;
return (
{
)}
{numSelected > 0 ? (
-
-
-
-
-
+
) : null}
);
@@ -157,6 +158,7 @@ const EnhancedTableToolbar = (props) => {
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.number.isRequired,
tableTitle: PropTypes.string,
+ selected: PropTypes.arrayOf(PropTypes.shape({}).isRequired).isRequired,
};
EnhancedTableToolbar.defaultProps = {
@@ -188,52 +190,97 @@ const useStyles = makeStyles((theme) => ({
selected: {
backgroundColor: `${lighten(theme.palette.primary.main, 0.85)} !important`,
},
+ hover: {
+ cursor: 'pointer',
+ },
}));
const ItemsTable = ({ items: rows, tableTitle }) => {
const classes = useStyles();
const { t } = useTranslation();
const { push } = useHistory();
- const [order, setOrder] = React.useState(ORDERING.ASC);
+ const [order, setOrder] = React.useState(ORDERING.DESC);
const [orderBy, setOrderBy] = React.useState('updatedAt');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
- const [rowsPerPage, setRowsPerPage] = React.useState(5);
+ const [rowsPerPage, setRowsPerPage] = React.useState(
+ ROWS_PER_PAGE_OPTIONS[0],
+ );
+
+ useEffect(() => {
+ // remove deleted rows from selection
+ const newSelected = selected.filter(
+ (id) => rows.findIndex(({ id: thisId }) => thisId === id) >= 0,
+ );
+ if (newSelected.length !== selected.length) {
+ setSelected(newSelected);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [rows]);
const headCells = [
{
id: 'name',
numeric: false,
- disablePadding: true,
label: t('Name'),
+ align: 'left',
},
{
id: 'type',
numeric: false,
- disablePadding: true,
label: t('Type'),
+ align: 'right',
},
{
id: 'createdAt',
numeric: false,
- disablePadding: true,
label: t('Created At'),
+ align: 'right',
+ type: ITEM_DATA_TYPES.DATE,
},
{
id: 'updatedAt',
numeric: false,
- disablePadding: true,
label: t('Updated At'),
+ align: 'right',
+ type: ITEM_DATA_TYPES.DATE,
+ },
+ {
+ id: 'actions',
+ numeric: false,
+ label: t('Actions'),
+ align: 'right',
},
];
- if (!rows.size) {
- return (
-
- {t('No Item Here')}
-
- );
- }
+ // display empty rows to maintain the table height
+ const emptyRows =
+ rowsPerPage - Math.min(rowsPerPage, rows.size - page * rowsPerPage);
+
+ // order and select rows to display given the current page and the number of entries displayed
+ const rowsToDisplay = getRowsForPage(
+ stableSort(rows, getComparator(order, orderBy)),
+ { page, rowsPerPage },
+ );
+
+ // transform rows' information into displayable information
+ const mappedRows = rowsToDisplay.map(
+ ({ id, name, updatedAt, createdAt, type }) => ({
+ id,
+ name,
+ type,
+ updatedAt,
+ createdAt,
+ actions: (
+ <>
+
+
+
+
+ >
+ ),
+ }),
+ );
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === ORDERING.ASC;
@@ -242,34 +289,34 @@ const ItemsTable = ({ items: rows, tableTitle }) => {
};
const handleSelectAllClick = (event) => {
- if (event.target.checked) {
- const newSelecteds = rows.map((n) => n.id).toJS();
- setSelected(newSelecteds);
- return;
+ const checked =
+ JSON.parse(event.target.dataset.indeterminate) || !event.target.checked;
+ if (!checked) {
+ const newSelecteds = mappedRows.map((n) => n.id).toJS();
+ return setSelected(newSelecteds);
}
- setSelected([]);
+ return setSelected([]);
};
- const handleClick = (event, id) => {
- const selectedIndex = selected.indexOf(id);
- let newSelected = [];
-
- if (selectedIndex === -1) {
- newSelected = newSelected.concat(selected, id);
- } else if (selectedIndex === 0) {
- newSelected = newSelected.concat(selected.slice(1));
- } else if (selectedIndex === selected.length - 1) {
- newSelected = newSelected.concat(selected.slice(0, -1));
- } else if (selectedIndex > 0) {
- newSelected = newSelected.concat(
- selected.slice(0, selectedIndex),
- selected.slice(selectedIndex + 1),
- );
- }
+ const removeItemsFromSelected = (items) => {
+ const newSelected = selected.filter((id) => !items.includes(id));
+ setSelected(newSelected);
+ };
+ const addItemsInSelected = (items) => {
+ const newSelected = selected.concat(items);
setSelected(newSelected);
};
+ const handleClick = (event, id) => {
+ const checked = selected.indexOf(id) !== -1;
+ if (checked) {
+ removeItemsFromSelected([id]);
+ } else {
+ addItemsInSelected([id]);
+ }
+ };
+
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -283,19 +330,17 @@ const ItemsTable = ({ items: rows, tableTitle }) => {
push(buildItemPath(id));
};
- const isSelected = (id) => selected.indexOf(id) !== -1;
-
- const emptyRows =
- rowsPerPage - Math.min(rowsPerPage, rows.size - page * rowsPerPage);
+ // format entry data given type
+ const formatRowValue = ({ value, type }) => {
+ switch (type) {
+ case ITEM_DATA_TYPES.DATE:
+ return formatDate(value);
+ default:
+ return value;
+ }
+ };
- // transform rows' information into displayable information
- const mappedRows = rows.map(({ id, name, updatedAt, createdAt, type }) => ({
- id,
- name,
- type,
- updatedAt: formatDate(updatedAt),
- createdAt: formatDate(createdAt),
- }));
+ const isSelected = (id) => selected.indexOf(id) !== -1;
return (
@@ -303,6 +348,7 @@ const ItemsTable = ({ items: rows, tableTitle }) => {
{
headCells={headCells}
/>
- {stableSort(mappedRows, getComparator(order, orderBy))
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((row, index) => {
- const isItemSelected = isSelected(row.id);
- const labelId = `enhanced-table-checkbox-${index}`;
+ {mappedRows.map((row, index) => {
+ const isItemSelected = isSelected(row.id);
+ const labelId = `enhanced-table-checkbox-${index}`;
- return (
-
-
- handleClick(event, row.id)}
- color="primary"
- />
-
- {
- // does not render name
- headCells.map(({ id: field }) => (
- handleRowOnClick(row.id)}
- >
- {row[field]}
-
- ))
- }
-
- );
- })}
+ return (
+
+
+ handleClick(event, row.id)}
+ color="primary"
+ />
+
+ {
+ // does not render name
+ headCells.map(({ id: field, align, type }, idx) => (
+ {
+ // do not navigate when clicking on actions
+ const shouldNavigate = idx !== headCells.length - 1;
+ if (shouldNavigate) {
+ handleRowOnClick(row.id);
+ }
+ }}
+ >
+ {formatRowValue({ value: row[field], type })}
+
+ ))
+ }
+
+ );
+ })}
{emptyRows > 0 && (
diff --git a/src/components/main/ShareItemModal.js b/src/components/main/ShareItemModal.js
index e34ef7b40..1bee4742d 100644
--- a/src/components/main/ShareItemModal.js
+++ b/src/components/main/ShareItemModal.js
@@ -88,7 +88,11 @@ const ShareItemModal = ({
label={t('Permission')}
>
{Object.values(PERMISSION_LEVELS).map((p) => (
-