Skip to content

Commit 1b2031f

Browse files
committed
move filtering to the data passed to the backend and fix pagination on the task analysis table
1 parent 8f495cd commit 1b2031f

4 files changed

Lines changed: 265 additions & 345 deletions

File tree

src/components/HOCs/WithFilterCriteria/WithFilterCriteria.jsx

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,22 @@ export default function WithFilterCriteria(
5353

5454
updateCriteria = (newCriteria) => {
5555
const criteria = _cloneDeep(this.state.criteria);
56-
criteria.sortCriteria = newCriteria.sortCriteria;
57-
criteria.page = newCriteria.page;
58-
criteria.filters = newCriteria.filters;
59-
criteria.includeTags = newCriteria.includeTags;
56+
57+
if (newCriteria.hasOwnProperty("sortCriteria")) {
58+
criteria.sortCriteria = newCriteria.sortCriteria;
59+
}
60+
if (newCriteria.hasOwnProperty("page")) {
61+
criteria.page = newCriteria.page;
62+
}
63+
if (newCriteria.hasOwnProperty("filters")) {
64+
criteria.filters = newCriteria.filters;
65+
}
66+
if (newCriteria.hasOwnProperty("includeTags")) {
67+
criteria.includeTags = newCriteria.includeTags;
68+
}
69+
if (newCriteria.hasOwnProperty("pageSize")) {
70+
criteria.pageSize = newCriteria.pageSize;
71+
}
6072

6173
this.setState({ criteria });
6274
if (this.props.setSearchFilters) {
@@ -73,6 +85,9 @@ export default function WithFilterCriteria(
7385

7486
updateTaskPropertyCriteria = (propertySearch) => {
7587
const criteria = _cloneDeep(this.state.criteria);
88+
if (!criteria.filters) {
89+
criteria.filters = {};
90+
}
7691
criteria.filters.taskPropertySearch = propertySearch;
7792
this.setState({ criteria });
7893
};
@@ -89,6 +104,9 @@ export default function WithFilterCriteria(
89104

90105
clearTaskPropertyCriteria = () => {
91106
const criteria = _cloneDeep(this.state.criteria);
107+
if (!criteria.filters) {
108+
criteria.filters = {};
109+
}
92110
criteria.filters.taskPropertySearch = null;
93111
this.setState({ criteria });
94112
};
@@ -125,11 +143,15 @@ export default function WithFilterCriteria(
125143
changePageSize = (pageSize) => {
126144
const typedCriteria = _cloneDeep(this.state.criteria);
127145
typedCriteria.pageSize = pageSize;
146+
typedCriteria.page = 0;
128147
this.setState({ criteria: typedCriteria });
129148
};
130149

131150
setFiltered = (column, value) => {
132151
const typedCriteria = _cloneDeep(this.state.criteria);
152+
if (!typedCriteria.filters) {
153+
typedCriteria.filters = {};
154+
}
133155
typedCriteria.filters[column] = value;
134156

135157
//Reset Page so it goes back to 0
@@ -139,6 +161,11 @@ export default function WithFilterCriteria(
139161

140162
updateIncludedFilters(props, criteria = {}) {
141163
const typedCriteria = _merge({}, criteria, _cloneDeep(this.state.criteria));
164+
165+
if (!typedCriteria.filters) {
166+
typedCriteria.filters = {};
167+
}
168+
142169
typedCriteria.filters["status"] = _keys(_pickBy(props.includeTaskStatuses, (s) => s));
143170
typedCriteria.filters["reviewStatus"] = _keys(
144171
_pickBy(props.includeTaskReviewStatuses, (r) => r),
@@ -188,6 +215,10 @@ export default function WithFilterCriteria(
188215

189216
const criteria = typedCriteria || _cloneDeep(this.state.criteria);
190217

218+
// Ensure filters object exists before setting properties
219+
if (!criteria.filters) {
220+
criteria.filters = {};
221+
}
191222
criteria.filters.archived = true;
192223

193224
this.debouncedTasksFetch(challengeId, criteria, this.state.criteria.pageSize);

src/components/PaginationControl/PaginationControl.jsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
import { FormattedMessage, injectIntl } from "react-intl";
22
import messages from "./Messages";
33

4-
function PaginationControl({ currentPage, totalPages, pageSize, gotoPage, setPageSize, intl }) {
5-
const canPreviousPage = currentPage > 0;
6-
const canNextPage = currentPage < totalPages - 1;
7-
8-
const previousPage = () => gotoPage(Math.max(currentPage - 1, 0));
9-
const nextPage = () => gotoPage(Math.min(currentPage + 1, totalPages - 1));
10-
4+
function PaginationControl({
5+
currentPage,
6+
pageCount,
7+
pageSize,
8+
gotoPage,
9+
setPageSize,
10+
previousPage,
11+
nextPage,
12+
intl,
13+
}) {
1114
return (
1215
<div className="mr-flex mr-flex-wrap mr-justify-center mr-items-center mr-gap-2 md:mr-gap-3 mr-mt-6 mr-text-white">
1316
<button
1417
className="mr-button mr-button--small mr-mb-2"
1518
onClick={() => gotoPage(0)}
16-
disabled={!canPreviousPage}
19+
disabled={!(currentPage > 0)}
1720
>
1821
{"<< "}
1922
<FormattedMessage {...messages.first} />
2023
</button>
2124
<button
2225
className="mr-button mr-button--small mr-mb-2"
23-
onClick={() => previousPage()}
24-
disabled={!canPreviousPage}
26+
onClick={previousPage}
27+
disabled={!(currentPage > 0)}
2528
>
2629
{"< "}
2730
<FormattedMessage {...messages.previous} />
@@ -32,24 +35,24 @@ function PaginationControl({ currentPage, totalPages, pageSize, gotoPage, setPag
3235
className="mr-input mr-px-1 mr-py-0 mr-w-12 md:mr-w-16"
3336
type="number"
3437
min={1}
35-
max={totalPages}
38+
max={pageCount}
3639
value={currentPage + 1}
3740
onChange={(e) => gotoPage(e.target.value ? Number(e.target.value) - 1 : 0)}
3841
/>{" "}
39-
<FormattedMessage {...messages.of} /> {totalPages}
42+
<FormattedMessage {...messages.of} /> {pageCount}
4043
</span>
4144
<button
4245
className="mr-button mr-button--small mr-mb-2"
43-
onClick={() => nextPage()}
44-
disabled={!canNextPage}
46+
onClick={nextPage}
47+
disabled={!(currentPage < pageCount - 1)}
4548
>
4649
<FormattedMessage {...messages.next} />
4750
{" >"}
4851
</button>
4952
<button
5053
className="mr-button mr-button--small mr-mb-2"
51-
onClick={() => gotoPage(totalPages - 1)}
52-
disabled={!canNextPage}
54+
onClick={() => gotoPage(pageCount - 1)}
55+
disabled={!(currentPage < pageCount - 1)}
5356
>
5457
<FormattedMessage {...messages.last} />
5558
{" >>"}
Lines changed: 68 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect } from "react";
1+
import React, { useState, useEffect, Fragment } from "react";
22
import "./EnhancedTable.scss";
33
import { headerStyles, inputStyles, sortableHeaderStyles, tableWrapperStyles } from "./TableStyles";
44

@@ -58,102 +58,78 @@ export const SearchFilter = ({ value, onChange, placeholder, inputClassName = ""
5858
/**
5959
* Renders a consistent table header from react-table's headerGroups
6060
*/
61-
export const renderTableHeader = (headerGroups) => {
62-
return (
63-
<>
64-
{headerGroups.map((headerGroup, index) => (
65-
<tr {...headerGroup.getHeaderGroupProps()} key={`header-row-${headerGroup.id || index}`}>
66-
{headerGroup.headers.map((column) => {
67-
const headerProps = column.getHeaderProps();
68-
const sortByProps = column.getSortByToggleProps ? column.getSortByToggleProps() : {};
61+
export const renderTableHeader = (headerGroups, props) => {
62+
const handleSortChange = (columnId) => {
63+
if (!props.updateCriteria) return;
64+
65+
const currentSort = props.criteria?.sortCriteria;
66+
let newSortCriteria;
67+
68+
if (!currentSort || currentSort.sortBy !== columnId) {
69+
newSortCriteria = { sortBy: columnId, direction: "ASC" };
70+
} else if (currentSort.direction === "ASC") {
71+
newSortCriteria = { sortBy: columnId, direction: "DESC" };
72+
} else {
73+
newSortCriteria = { sortBy: "name", direction: "DESC" };
74+
}
6975

70-
// Make sure to prevent click event conflicts
71-
const onHeaderClick = (e) => {
72-
if (column.getSortByToggleProps && !column.disableSortBy) {
73-
sortByProps.onClick(e);
74-
}
75-
};
76+
props.updateCriteria({ sortCriteria: newSortCriteria });
77+
};
78+
const currentSort = props.criteria?.sortCriteria;
7679

77-
return (
80+
return (
81+
<>
82+
{headerGroups.map((headerGroup, headerGroupIndex) => (
83+
<Fragment key={`header-group-${headerGroupIndex}`}>
84+
<tr {...headerGroup.getHeaderGroupProps()}>
85+
{headerGroup.headers.map((column, columnIndex) => (
7886
<th
79-
key={`header-cell-${column.id}`}
80-
className={`${headerStyles} ${!column.disableSortBy ? sortableHeaderStyles : ""}`}
81-
{...headerProps}
82-
onClick={onHeaderClick}
87+
{...column.getHeaderProps()}
88+
className={`mr-px-2 mr-text-left mr-border-gray-600 mr-relative ${column.canResize ? "mr-border-r mr-border-gray-500" : ""}`}
89+
key={`header-${column.id}-${columnIndex}`}
8390
style={{
84-
...headerProps.style,
91+
...column.getHeaderProps().style,
8592
width: column.width,
8693
minWidth: column.minWidth,
87-
position: "relative",
94+
overflow: "hidden",
8895
}}
8996
>
90-
<div className="mr-header-content">
91-
<div className="mr-flex mr-items-center mr-justify-between">
92-
<div
93-
className="mr-flex mr-items-center mr-whitespace-nowrap"
94-
style={{
95-
overflow: "hidden",
96-
textOverflow: "ellipsis",
97-
}}
97+
<div
98+
className="mr-relative mr-overflow-hidden"
99+
style={{ paddingRight: !column.disableSortBy ? "24px" : "8px" }}
100+
>
101+
<div className="mr-truncate">{column.render("Header")}</div>
102+
{!column.disableSortBy && (
103+
<button
104+
className="mr-absolute mr-right-0 mr-top-0 mr-bottom-0 mr-w-6 mr-h-full mr-flex mr-items-center mr-justify-center mr-text-gray-400 hover:mr-text-white mr-cursor-pointer mr-text-xs mr-z-20"
105+
onClick={() => handleSortChange(column.id)}
106+
title={`Sort by ${column.Header || column.id}`}
98107
>
99-
<span>{column.render("Header")}</span>
100-
{!column.disableSortBy && (
101-
<span className="mr-ml-1 mr-opacity-70">
102-
{column.isSorted ? (
103-
column.isSortedDesc ? (
104-
" ▼"
105-
) : (
106-
" ▲"
107-
)
108-
) : (
109-
<span className="mr-text-xs mr-opacity-50 mr-inline-block"></span>
110-
)}
111-
</span>
112-
)}
113-
</div>
114-
</div>
115-
{!column.disableResizing && (
116-
<div
117-
className={`mr-resizer`}
118-
{...column.getResizerProps?.()}
119-
onClick={(e) => {
120-
e.stopPropagation();
121-
}}
122-
/>
108+
{!currentSort || currentSort.sortBy !== column.id
109+
? "↕"
110+
: currentSort.direction === "DESC"
111+
? "▼"
112+
: "▲"}
113+
</button>
123114
)}
124115
</div>
116+
{column.canResize && (
117+
<div
118+
{...column.getResizerProps()}
119+
className="mr-absolute mr-right-0 mr-top-0 mr-w-1 mr-h-full mr-bg-gray-400 mr-cursor-col-resize hover:mr-bg-blue-400 hover:mr-scale-x-3 mr-transition-all mr-z-10"
120+
/>
121+
)}
125122
</th>
126-
);
127-
})}
128-
</tr>
129-
))}
130-
131-
{/* Add a separate row for filters */}
132-
{headerGroups.map((headerGroup, index) => (
133-
<tr key={`filter-row-${headerGroup.id}-${index}`}>
134-
{headerGroup.headers.map((column) => (
135-
<td
136-
key={`filter-cell-${column.id}`}
137-
style={{
138-
width: column.width,
139-
minWidth: column.minWidth,
140-
}}
141-
>
142-
{column.canFilter && (
143-
<div
144-
className="mr-header-filter mr-mr-2"
145-
onClick={(e) => e.stopPropagation()}
146-
style={{
147-
overflow: "hidden",
148-
maxWidth: "100%",
149-
}}
150-
>
151-
{column.render("Filter")}
152-
</div>
153-
)}
154-
</td>
155-
))}
156-
</tr>
123+
))}
124+
</tr>
125+
<tr>
126+
{headerGroup.headers.map((column, columnIndex) => (
127+
<th key={`filter-${column.id}-${columnIndex}`} className="mr-px-2">
128+
<div>{column.Filter ? column.render("Filter") : null}</div>
129+
</th>
130+
))}
131+
</tr>
132+
</Fragment>
157133
))}
158134
</>
159135
);
@@ -177,30 +153,20 @@ export const TableWrapper = ({ children, className = "" }) => (
177153
* @param {Object} options - Additional styling options
178154
* @returns {JSX.Element} - The rendered table cell
179155
*/
180-
export const renderTableCell = (cell, options = {}) => {
156+
export const renderTableCell = (cell, row, cellIndex) => {
181157
return (
182158
<td
159+
key={`cell-${row.original.id}-${cell.column.id}-${cellIndex}`}
183160
{...cell.getCellProps()}
161+
className="mr-px-2"
184162
style={{
185163
...cell.getCellProps().style,
186-
whiteSpace: "nowrap !important",
187-
overflow: "hidden !important",
188-
textOverflow: "ellipsis !important",
189-
minWidth: cell.column.minWidth,
190-
...options,
164+
overflow: "hidden",
165+
textOverflow: "ellipsis",
166+
whiteSpace: "nowrap",
191167
}}
192168
>
193-
<div
194-
style={{
195-
overflow: "hidden !important",
196-
textOverflow: "ellipsis !important",
197-
whiteSpace: "nowrap !important",
198-
width: "100%",
199-
display: "block",
200-
}}
201-
>
202-
{cell.render("Cell")}
203-
</div>
169+
{cell.render("Cell")}
204170
</td>
205171
);
206172
};

0 commit comments

Comments
 (0)