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/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 ( -
+
)} -
-
-
-
- - - {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={8} + 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/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; 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 && ( { 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, { - 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); @@ -44,7 +46,6 @@ export const UserGroupReport = () => { ...oldUsersArray, { ...i, stats: res }, ]); - setLoading(true); }) .catch((error) => { if (error.response.status === 500) { @@ -53,9 +54,6 @@ export const UserGroupReport = () => { setFormError(error.response.data.detail[0]["msg"]); } }) - .finally(() => { - setLoading(false); - }) ); }, [userGroupFormData] @@ -78,14 +76,18 @@ export const UserGroupReport = () => { formError={formError} setFormError={setFormError} /> - {(isLoading || loading) && ( -
Loading...
+ {isLoading && ( +
+ + Loading... +
)} {data && ( 0} + loading={userIds.length !== users.length} /> )}