Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Modify user group report results logic #96

Merged
merged 4 commits into from
May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/assets/svgIcons/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { BanIcon } from "./ban";
export { SortIcon, SortDownIcon, SortUpIcon } from "./sortIcons";
export { SpinnerIcon } from "./spinner";
22 changes: 22 additions & 0 deletions src/assets/svgIcons/spinner.js
Original file line number Diff line number Diff line change
@@ -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 (
<svg
aria-hidden="true"
focusable="false"
role="img"
viewBox="0 0 512 512"
{...this.props}
>
<path
fill="currentColor"
d="M222.7 32.15C227.7 49.08 218.1 66.9 201.1 71.94C121.8 95.55 64 169.1 64 255.1C64 362 149.1 447.1 256 447.1C362 447.1 448 362 448 255.1C448 169.1 390.2 95.55 310.9 71.94C293.9 66.9 284.3 49.08 289.3 32.15C294.4 15.21 312.2 5.562 329.1 10.6C434.9 42.07 512 139.1 512 255.1C512 397.4 397.4 511.1 256 511.1C114.6 511.1 0 397.4 0 255.1C0 139.1 77.15 42.07 182.9 10.6C199.8 5.562 217.6 15.21 222.7 32.15V32.15z"
/>
</svg>
);
}
}
4 changes: 3 additions & 1 deletion src/components/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ export const DownloadCSVButton = ({ data, source }) => {
)}`;

return (
<div className="text-right m-4 text-lg">
<div
className={`text-right text-lg ${source === "mapathon" ? "m-4" : "my-4"}`}
>
<CSVLink
data={data}
headers={headers}
Expand Down
141 changes: 86 additions & 55 deletions src/components/userGroup/userGroupResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -28,63 +40,82 @@ export function UserGroupResultsTable({ columns, data, userDataCheck }) {
<UserGroupErrorMessage error={downloadError} />
</Error>
)}
<DownloadCSVButton data={data} source={"userGroup"} />
<div className="flex flex-col">
<div className="overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 inline-block min-w-full sm:px-6 lg:px-8">
<div className="overflow-x-auto"></div>
<table {...getTableProps()} className="min-w-full">
<thead className="border-b">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
scope="col"
className="text-xl font-semibold px-6 py-4 text-left"
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
>
<span className="inline ">
{column.render("Header")} &nbsp;
{column.canSort &&
(column.isSorted ? (
column.isSortedDesc ? (
<SortDownIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
<ReactPlaceholder
showLoadingAnimation
ready={rows.length > 0}
type="text"
rows={8}
className="mt-20 mx-4"
>
<div className="w-11/12 mx-auto">
<DownloadCSVButton data={data} source={"userGroup"} />
</div>
<div className="overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 inline-block min-w-full sm:px-6 lg:px-8">
<div className="overflow-x-auto" />
<table {...getTableProps()} className="w-11/12 mx-auto border">
<thead className="border-b">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
scope="col"
className="text-xl font-semibold p-4 text-center"
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
>
<span className="inline ">
{column.render("Header")} &nbsp;
{column.canSort &&
(column.isSorted ? (
column.isSortedDesc ? (
<SortDownIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
) : (
<SortUpIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
)
) : (
<SortUpIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
)
) : (
<SortIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
))}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()} className="border-b">
{row.cells.map((cell) => {
return (
<td
className="text-lg font-light px-6 py-4 whitespace-nowrap"
{...cell.getCellProps()}
>
{cell.render("Cell")}
</td>
);
})}
<SortIcon className="w-3 h-3 ml-1 inline text-blue-grey" />
))}
</span>
</th>
))}
</tr>
);
})}
</tbody>
</table>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()} className="border-b" key={i}>
{row.cells.map((cell) => {
return (
<td
className="text-lg font-light p-4 text-center whitespace-nowrap"
{...cell.getCellProps()}
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
{loading ? (
<div className="text-center w-11/12 mx-auto">
<SpinnerIcon className="animate-spin w-5 h-5 mt-2 inline text-red" />
</div>
) : (
<div className="bg-tan w-11/12 text-center mx-auto">
End of Table
</div>
)}
</div>
</div>
</div>
</ReactPlaceholder>
</div>
</>
);
Expand Down
15 changes: 4 additions & 11 deletions src/utils/userGroupUtils.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
11 changes: 9 additions & 2 deletions src/views/MapathonReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down Expand Up @@ -51,7 +52,10 @@ export const MapathonSummaryReport = (props) => {
<MapathonRedirectButton triggerFn={triggeredSubmit} />
</div>
{isLoading && (
<div className="mx-auto text-center w-1/4 p-1 mt-5">Loading...</div>
<div className="mx-auto text-center w-1/4 p-1 mt-5">
<SpinnerIcon className="animate-spin w-5 h-5 mr-2 mb-1 inline text-red" />
Loading...
</div>
)}
{data && <MapathonSummaryResults data={data} />}
</div>
Expand Down Expand Up @@ -80,7 +84,10 @@ export const MapathonDetailedReport = () => {
loading={isLoading || triggeredLoading}
/>
{(isLoading || triggeredLoading) && (
<div className="mx-auto text-center w-1/4 p-1 mt-5">Loading...</div>
<div className="mx-auto text-center w-1/4 p-1 mt-5">
<SpinnerIcon className="animate-spin w-5 h-5 mr-2 mb-1 inline text-red" />
Loading...
</div>
)}
{data && (
<MapathonDetailedResultsTable
Expand Down
24 changes: 13 additions & 11 deletions src/views/UserGroupReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getUserIds, getUserStats } from "../queries/getUserStats";
import { MiniNavBar } from "../components/nav/navbar";
import { UserGroupColumnHeadings } from "../components/userGroup/constants";
import { aggregateUserGroupData } from "../utils/userGroupUtils";
import { SpinnerIcon } from "../assets/svgIcons";

const userGroupPage = [
{ pageTitle: "User Group Report", pageURL: "/user-report" },
Expand All @@ -18,18 +19,19 @@ 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, {
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);
Expand All @@ -44,7 +46,6 @@ export const UserGroupReport = () => {
...oldUsersArray,
{ ...i, stats: res },
]);
setLoading(true);
})
.catch((error) => {
if (error.response.status === 500) {
Expand All @@ -53,9 +54,6 @@ export const UserGroupReport = () => {
setFormError(error.response.data.detail[0]["msg"]);
}
})
.finally(() => {
setLoading(false);
})
);
},
[userGroupFormData]
Expand All @@ -78,14 +76,18 @@ export const UserGroupReport = () => {
formError={formError}
setFormError={setFormError}
/>
{(isLoading || loading) && (
<div className="mx-auto text-center w-1/4 p-1 mt-5">Loading...</div>
{isLoading && (
<div className="mx-auto text-center w-1/4 p-1 mt-5">
<SpinnerIcon className="animate-spin w-5 h-5 mr-2 mb-1 inline text-red" />
Loading...
</div>
)}
{data && (
<UserGroupResultsTable
columns={UserGroupColumnHeadings}
data={aggregateUserGroupData(users)}
userDataCheck={userIds && userIds.length > 0}
loading={userIds.length !== users.length}
/>
)}
</div>
Expand Down