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