From 4e1b76316144c7bdf56693de0dd4f1b0dbf55f58 Mon Sep 17 00:00:00 2001 From: Macaulay Precious <87583799+Precious-Macaulay@users.noreply.github.com> Date: Sat, 24 Feb 2024 22:42:16 +0100 Subject: [PATCH] Allow user sort (#1075) * Allow user to sort issue #1013 Fix * added pmd file to root * fix sorting issue in task table --- frontend/src/components/task/task-table.js | 177 +++++++++++++++++---- pmd.xml | 12 ++ 2 files changed, 158 insertions(+), 31 deletions(-) create mode 100644 pmd.xml diff --git a/frontend/src/components/task/task-table.js b/frontend/src/components/task/task-table.js index b03769f0..89218fe5 100644 --- a/frontend/src/components/task/task-table.js +++ b/frontend/src/components/task/task-table.js @@ -157,6 +157,79 @@ const styles = theme => ({ }, }) +const tableHeaderMetadata = { + "task.table.head.task": { sortable: true, numeric: false, dataBaseKey: "title" }, + "task.table.head.status": { sortable: true, numeric: false, dataBaseKey: "status" }, + "task.table.head.project": { sortable: true, numeric: false, dataBaseKey: "Project.name" }, + "task.table.head.value": { sortable: true, numeric: true, dataBaseKey: "value" }, + "task.table.head.labels": { sortable: true, numeric: false, dataBaseKey: "Labels" }, + "task.table.head.createdAt": { sortable: true, numeric: false, dataBaseKey: "createdAt" } +} + +const getSortingValue = (item, fieldId) => { + + const getValue = (item, dataBaseKey) => { + const keys = dataBaseKey.split("."); + return keys.reduce((obj, key) => (obj && obj[key] !== 'undefined') ? obj[key] : undefined, item); + }; + + const metadata = tableHeaderMetadata[fieldId]; + if (!metadata) { + console.error(`No metadata found for fieldId: ${fieldId}`); + return null; + } + + const { numeric, dataBaseKey } = metadata; + + const value = getValue(item, dataBaseKey); + + if (value === undefined) { + console.error(`Failed to get value for fieldId: ${fieldId}`); + return null; + } + + if (numeric) { + const parsedValue = parseFloat(value); + if (isNaN(parsedValue)) { + console.error(`Failed to parse numeric value for fieldId: ${fieldId}`); + return null; + } + return parsedValue; + } + return value; +}; + +const sortData = (data, sortedBy, sortDirection) => { + if (sortDirection === 'none') return data; + + return [...data].sort((a, b) => { + let aValue = getSortingValue(a, sortedBy); + let bValue = getSortingValue(b, sortedBy); + + // Handle null values + if (aValue === null || bValue === null) { + return (aValue === null ? (sortDirection === 'asc' ? -1 : 1) : (sortDirection === 'asc' ? 1 : -1)); + } + + // Handle date sorting + if (sortedBy === 'task.table.head.createdAt') { + let aDate = new Date(aValue).getTime(); + let bDate = new Date(bValue).getTime(); + return (sortDirection === 'asc' ? aDate - bDate : bDate - aDate); + } + + // Handle labels array sorting + if (sortedBy === 'task.table.head.labels') { + aValue = aValue.map(label => label.name).join(''); + bValue = bValue.map(label => label.name).join(''); + } + + // Handle string sorting + let comparator = String(aValue).localeCompare(String(bValue), 'en', { numeric: true, sensitivity: 'base', ignorePunctuation: true }); + return (sortDirection === 'asc' ? comparator : -comparator); + }); +}; + class CustomPaginationActionsTable extends React.Component { constructor(props) { super(props) @@ -164,9 +237,41 @@ class CustomPaginationActionsTable extends React.Component { this.state = { page: 0, rowsPerPage: 10, + sortedBy: null, + sortDirection: 'asc', + sortedData: this.props.tasks.data + } + } + + componentDidUpdate(prevProps) { + if (prevProps.tasks !== this.props.tasks) { + const { sortedBy, sortDirection } = this.state; + const newSortedData = sortData(this.props.tasks.data, sortedBy, sortDirection); + this.setState({ + sortedData: newSortedData + }); } } + handleSort = (fieldId, sortDirection) => { + const newSortedData = sortData(this.props.tasks.data, fieldId, sortDirection); + + return { + sortedBy: fieldId, + sortDirection, + sortedData: newSortedData, + }; + } + + sortHandler = (fieldId) => { + this.setState((prevState) => { + const { sortedBy, sortDirection } = prevState; + const newSortDirection = sortedBy === fieldId ? (sortDirection === 'asc' ? 'desc' : (sortDirection === 'desc' ? 'none' : 'asc')) : 'asc'; + return this.handleSort(fieldId, newSortDirection); + }); + }; + + handleChangePage = (event, page) => { this.setState({ page }) }; @@ -188,18 +293,49 @@ class CustomPaginationActionsTable extends React.Component { render() { const { classes, tasks } = this.props - const { rowsPerPage, page } = this.state - const emptyRows = tasks.data.length ? rowsPerPage - Math.min(rowsPerPage, tasks.data.length - page * rowsPerPage) : 0 + const { rowsPerPage, page, sortedBy, sortDirection, sortedData } = this.state; + + const emptyRows = sortedData.length ? rowsPerPage - Math.min(rowsPerPage, sortedData.length - page * rowsPerPage) : 0 + const TableCellWithSortLogic = ({ fieldId, defineMessage, sortHandler }) => { + return ( + { + return sortHandler(fieldId) + } + } + > + + + ) + } + + const TableHeadCustom = () => { + console.log() + return ( + + + {Object.entries(tableHeaderMetadata).map(([fieldId, metadata]) => ( + + + + ))} + + + ); + }; if (tasks.completed && tasks.data.length === 0) { - + return (
-
+
); } return ( @@ -207,30 +343,9 @@ class CustomPaginationActionsTable extends React.Component {
- - - - - - - - - - - - - - - - - - - - - - + - {tasks.data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => { + {sortedData.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => { const assigned = n.Assigns.find(a => a.id === n.assigned) const assignedUser = assigned && assigned.User return ( @@ -298,11 +413,11 @@ class CustomPaginationActionsTable extends React.Component { this.handleChangePage(e, page)} - onChangeRowsPerPage={(e, page) => this.handleChangeRowsPerPage(e, page)} + onPageChange={(e, page) => this.handleChangePage(e, page)} + onRowsPerPageChange={(e, page) => this.handleChangeRowsPerPage(e, page)} Actions={TablePaginationActionsWrapped} /> @@ -322,4 +437,4 @@ CustomPaginationActionsTable.propTypes = { user: PropTypes.object, } -export default injectIntl(withRouter(withStyles(styles)(CustomPaginationActionsTable))) +export default injectIntl(withRouter(withStyles(styles)(CustomPaginationActionsTable))) \ No newline at end of file diff --git a/pmd.xml b/pmd.xml new file mode 100644 index 00000000..f39b32c2 --- /dev/null +++ b/pmd.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file