From 2488804d26bd309e6782a3463d6d62e6d0613f70 Mon Sep 17 00:00:00 2001 From: Diana Rita Nanyanzi <31903212+d-rita@users.noreply.github.com> Date: Wed, 11 May 2022 00:53:56 +0300 Subject: [PATCH 1/4] Add loading spinner and placeholder as data loads --- package-lock.json | 15 +++ package.json | 1 + src/assets/svgIcons/index.js | 1 + src/assets/svgIcons/spinner.js | 22 ++++ src/components/userGroup/userGroupResults.js | 131 +++++++++++-------- src/views/MapathonReports.js | 11 +- src/views/UserGroupReport.js | 6 +- 7 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 src/assets/svgIcons/spinner.js diff --git a/package-lock.json b/package-lock.json index 2965c40..749bb05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react-datepicker": "^4.2.1", "react-dom": "^17.0.2", "react-intl": "^5.20.12", + "react-placeholder": "^4.1.0", "react-query": "^3.27.0", "react-redux": "^7.2.5", "react-router-dom": "^5.3.0", @@ -18483,6 +18484,14 @@ "react-dom": "^15.5.x || ^16.x || ^17.x" } }, + "node_modules/react-placeholder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-placeholder/-/react-placeholder-4.1.0.tgz", + "integrity": "sha512-z1HGD86NWJTYTQumHsmGH9jkozv4QHa9dju/vHVUd4f1svu23pf5v7QoBLBfs3kA1S9GLJaCeRMHLbO2SCdz5A==", + "peerDependencies": { + "react": "^16.8.0 || ^17" + } + }, "node_modules/react-popper": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz", @@ -38342,6 +38351,12 @@ "integrity": "sha512-oPlOTYcISLHfpMog2lUZMFSbqOs4LFcA4+vo7fpfevB5v9Z0D5VBDBkfeO5lv+hpEcGoaGk67braLT+QT+eICA==", "requires": {} }, + "react-placeholder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-placeholder/-/react-placeholder-4.1.0.tgz", + "integrity": "sha512-z1HGD86NWJTYTQumHsmGH9jkozv4QHa9dju/vHVUd4f1svu23pf5v7QoBLBfs3kA1S9GLJaCeRMHLbO2SCdz5A==", + "requires": {} + }, "react-popper": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz", diff --git a/package.json b/package.json index a37ce23..2c1051a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "react-datepicker": "^4.2.1", "react-dom": "^17.0.2", "react-intl": "^5.20.12", + "react-placeholder": "^4.1.0", "react-query": "^3.27.0", "react-redux": "^7.2.5", "react-router-dom": "^5.3.0", diff --git a/src/assets/svgIcons/index.js b/src/assets/svgIcons/index.js index 4af57cd..965f1b0 100644 --- a/src/assets/svgIcons/index.js +++ b/src/assets/svgIcons/index.js @@ -1,2 +1,3 @@ export { BanIcon } from "./ban"; export { SortIcon, SortDownIcon, SortUpIcon } from "./sortIcons"; +export { SpinnerIcon } from "./spinner"; diff --git a/src/assets/svgIcons/spinner.js b/src/assets/svgIcons/spinner.js new file mode 100644 index 0000000..c09c0d6 --- /dev/null +++ b/src/assets/svgIcons/spinner.js @@ -0,0 +1,22 @@ +import React from "react"; + +// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/ +// License: CC-By 4.0 +export class SpinnerIcon extends React.PureComponent { + render() { + return ( + + ); + } +} diff --git a/src/components/userGroup/userGroupResults.js b/src/components/userGroup/userGroupResults.js index 33d3e31..fb31bbb 100644 --- a/src/components/userGroup/userGroupResults.js +++ b/src/components/userGroup/userGroupResults.js @@ -2,13 +2,25 @@ import React from "react"; import { useSelector } from "react-redux"; import { useTable, useSortBy } from "react-table"; import { FormattedMessage } from "react-intl"; +import ReactPlaceholder from "react-placeholder"; import messages from "./messages"; import { Error } from "../formResponses"; import { UserGroupErrorMessage } from "./userGroupError"; -import { SortDownIcon, SortIcon, SortUpIcon } from "../../assets/svgIcons"; +import { + SortDownIcon, + SortIcon, + SortUpIcon, + SpinnerIcon, +} from "../../assets/svgIcons"; import { DownloadCSVButton } from "../download"; +import "react-placeholder/lib/reactPlaceholder.css"; -export function UserGroupResultsTable({ columns, data, userDataCheck }) { +export function UserGroupResultsTable({ + columns, + data, + userDataCheck, + loading, +}) { const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable( { @@ -28,63 +40,72 @@ export function UserGroupResultsTable({ columns, data, userDataCheck }) { )} -
-
-
-
- - - {headerGroups.map((headerGroup) => ( - - {headerGroup.headers.map((column) => ( - + + {rows.map((row, i) => { + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + ); + })} + + ); + })} + +
- - {column.render("Header")}   - {column.canSort && - (column.isSorted ? ( - column.isSortedDesc ? ( - + 0} + type="text" + rows={5} + className="mt-20 mx-4" + > + +
+
+
+ + + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + - ))} - - ))} - - - {rows.map((row, i) => { - prepareRow(row); - return ( - - {row.cells.map((cell) => { - return ( - - ); - })} + + ))} + + + ))} - ); - })} - -
+ + {column.render("Header")}   + {column.canSort && + (column.isSorted ? ( + column.isSortedDesc ? ( + + ) : ( + + ) ) : ( - - ) - ) : ( - - ))} - -
- {cell.render("Cell")} -
+ ))} +
+ {cell.render("Cell")} +
+ {/* {loading ? (
) : (
End of Table
)} */} +
-
+ ); diff --git a/src/views/MapathonReports.js b/src/views/MapathonReports.js index 57d3fa2..1f20e10 100644 --- a/src/views/MapathonReports.js +++ b/src/views/MapathonReports.js @@ -15,6 +15,7 @@ import { setTriggerSubmit } from "../features/form/formSlice"; import { MiniNavBar } from "../components/nav/navbar"; import { aggregateMapathonUserData } from "../utils/mapathonDataUtils"; import { MapathonDetailedTableHeaders } from "../components/mapathon/constants"; +import { SpinnerIcon } from "../assets/svgIcons"; const MAPATHON_PAGES = [ { pageTitle: "Mapathon Summary Report", pageURL: "/mapathon-report/summary" }, @@ -51,7 +52,10 @@ export const MapathonSummaryReport = (props) => { {isLoading && ( -
Loading...
+
+ + Loading... +
)} {data && } @@ -80,7 +84,10 @@ export const MapathonDetailedReport = () => { loading={isLoading || triggeredLoading} /> {(isLoading || triggeredLoading) && ( -
Loading...
+
+ + Loading... +
)} {data && ( { setFormError={setFormError} /> {(isLoading || loading) && ( -
Loading...
+
+ + Loading... +
)} {data && ( Date: Thu, 12 May 2022 00:24:11 +0300 Subject: [PATCH 2/4] Add row loading spinner + center user group table results --- src/components/download.js | 4 +++- src/components/userGroup/userGroupResults.js | 22 ++++++++++++++------ src/views/UserGroupReport.js | 8 ++----- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/components/download.js b/src/components/download.js index 1aa0b45..09044fd 100644 --- a/src/components/download.js +++ b/src/components/download.js @@ -105,7 +105,9 @@ export const DownloadCSVButton = ({ data, source }) => { )}`; return ( -
+
0} type="text" - rows={5} + rows={8} className="mt-20 mx-4" > - +
+ +
- +
{headerGroups.map((headerGroup) => ( {headerGroup.headers.map((column) => (
{ return ( {cell.render("Cell")} @@ -102,7 +104,15 @@ export function UserGroupResultsTable({ })}
- {/* {loading ? (
) : (
End of Table
)} */} + {loading ? ( +
+ +
+ ) : ( +
+ End of Table +
+ )}
diff --git a/src/views/UserGroupReport.js b/src/views/UserGroupReport.js index 21b799a..762fb5e 100644 --- a/src/views/UserGroupReport.js +++ b/src/views/UserGroupReport.js @@ -19,7 +19,6 @@ export const UserGroupReport = () => { const { userGroupFormData, setUserGroupFormData } = useContext(FormContext); const [formError, setFormError] = useState(null); const [userIds, setUserIds] = useState([]); - const [loading, setLoading] = useState(false); const [users, setUsers] = useState(); const { mutate, data, isLoading } = useMutation(getUserIds, { @@ -45,7 +44,6 @@ export const UserGroupReport = () => { ...oldUsersArray, { ...i, stats: res }, ]); - setLoading(true); }) .catch((error) => { if (error.response.status === 500) { @@ -54,9 +52,6 @@ export const UserGroupReport = () => { setFormError(error.response.data.detail[0]["msg"]); } }) - .finally(() => { - setLoading(false); - }) ); }, [userGroupFormData] @@ -79,7 +74,7 @@ export const UserGroupReport = () => { formError={formError} setFormError={setFormError} /> - {(isLoading || loading) && ( + {isLoading && (
Loading... @@ -90,6 +85,7 @@ export const UserGroupReport = () => { columns={UserGroupColumnHeadings} data={aggregateUserGroupData(users)} userDataCheck={userIds && userIds.length > 0} + loading={userIds.length !== users.length} /> )}
From 22890ea926901e01d73fec4bd327cfd044c6c934 Mon Sep 17 00:00:00 2001 From: Diana Rita Nanyanzi <31903212+d-rita@users.noreply.github.com> Date: Tue, 17 May 2022 09:55:36 +0300 Subject: [PATCH 3/4] Modify error capturing logic in user report --- src/views/UserGroupReport.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/UserGroupReport.js b/src/views/UserGroupReport.js index 762fb5e..b6f8360 100644 --- a/src/views/UserGroupReport.js +++ b/src/views/UserGroupReport.js @@ -21,15 +21,17 @@ export const UserGroupReport = () => { const [userIds, setUserIds] = useState([]); const [users, setUsers] = useState(); - const { mutate, data, isLoading } = useMutation(getUserIds, { - onError: (error) => { + const { mutate, data, isLoading, error } = useMutation(getUserIds); + + useEffect(() => { + if (error) { if (error.response.status === 500) { setFormError(error.response.data); } else { setFormError(error.response.data.detail[0]["msg"]); } - }, - }); + } + }, [error]); useEffect(() => { if (data) setUserIds(data); From f51142bb4ec0fc6dece548262ab647020a23b83d Mon Sep 17 00:00:00 2001 From: Diana Rita Nanyanzi <31903212+d-rita@users.noreply.github.com> Date: Tue, 17 May 2022 10:32:43 +0300 Subject: [PATCH 4/4] Modify user group stats logic: - Due to the API endpoint upgrade, user stats can be retrieved directly - Remove redundant userGroup feature-count-and-filter util function - Map relevant stats directly to keys in data aggregation function --- src/utils/userGroupUtils.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/utils/userGroupUtils.js b/src/utils/userGroupUtils.js index d7bcc2f..eb982dc 100644 --- a/src/utils/userGroupUtils.js +++ b/src/utils/userGroupUtils.js @@ -1,19 +1,12 @@ -export const featureActionCount = (array, feature, action) => { - let x = array.filter( - (i) => i["feature"] === feature && i["action"] === action - ); - return x[0] ? x[0]["count"] : 0; -}; - export const aggregateUserGroupData = (obj) => { const arr = []; obj.forEach((i) => { arr.push({ ...i, - createdBuildings: featureActionCount(i["stats"], "building", "create"), - modifiedBuildings: featureActionCount(i["stats"], "building", "modify"), - createdHighways: featureActionCount(i["stats"], "highway", "create"), - modifiedHighways: featureActionCount(i["stats"], "highway", "modify"), + createdBuildings: i["stats"][0]["addedBuildings"], + modifiedBuildings: i["stats"][0]["modifiedBuildings"], + createdHighways: i["stats"][0]["addedHighway"], + modifiedHighways: i["stats"][0]["modifiedHighway"], }); }); return arr;