Skip to content

Commit

Permalink
Added button for CSV export for admins (#596)
Browse files Browse the repository at this point in the history
  • Loading branch information
asun555 authored Jul 7, 2024
1 parent 301a6bb commit 9699b3b
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 16 deletions.
2 changes: 2 additions & 0 deletions uasc-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"prop-types": "^15.8.1",
"react": "^18",
"react-calendar": "^5.0.0",
"react-csv": "^2.2.2",
"react-dom": "^18",
"react-sweet-state": "^2.7.1",
"scheduler": "^0.23.2"
Expand All @@ -45,6 +46,7 @@
"@types/node": "^20.14.10",
"@types/prop-types": "^15",
"@types/react": "^18",
"@types/react-csv": "^1.1.10",
"@types/react-dom": "^18",
"@types/scheduler": "^0",
"eslint": "~8.57.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,21 @@ interface IAdminMemberView {
*/
fetchNextPage?: () => void

/**
* There are still more users to fetch
*/
hasNextPage?: boolean

/**
* Called when the *add new member* button is clicked
*/
openAddMemberView?: () => void

/**
* The action that should be performed when the export user data button is clicked
*/
exportUserDataHandler?: () => void

/*
* Used to indicate if there is currently an operation going on
*/
Expand Down Expand Up @@ -88,7 +99,9 @@ export const AdminMemberView = ({
rowOperations,
fetchNextPage,
openAddMemberView,
isUpdating
exportUserDataHandler,
isUpdating,
hasNextPage
}: IAdminMemberView) => {
/**
* For use with `AdminSearchBar`
Expand Down Expand Up @@ -157,10 +170,16 @@ export const AdminMemberView = ({
* We need to *scroll* to the next page of user data as it is assumed
* that the endpoint for fetching all users is paginated
*/
if (isLastPage || isQuerying) {
if (isLastPage || isQuerying || hasNextPage) {
fetchNextPage?.()
}
}, [isLastPage, fetchNextPage, isValidSearchQuery, shouldFilterByAccount])
}, [
isLastPage,
fetchNextPage,
isValidSearchQuery,
shouldFilterByAccount,
hasNextPage
])

const onSeachQueryChangedHandler = (newQuery: string) => {
setCurrentSearchQuery(newQuery)
Expand All @@ -179,9 +198,18 @@ export const AdminMemberView = ({
{filteredAccountType}
</Button>
</span>
<Button variant="default-sm" onClick={() => openAddMemberView?.()}>
Add New Member
</Button>
<span className="flex gap-2">
<Button variant="default-sm" onClick={() => openAddMemberView?.()}>
Add New Member
</Button>
<Button
variant="default-sm"
onClick={() => exportUserDataHandler?.()}
disabled={hasNextPage}
>
Export Data
</Button>
</span>
</span>
<Table<MemberColumnFormat, "multiple-operations">
data={(data && dataFilter(data)) || [defaultData]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import AdminUserCreationModal, {
AccountType
} from "./AdminUserCreation/AdminUserCreationModal"
import ModalContainer from "@/components/generic/ModalContainer/ModalContainer"
import { useState } from "react"
import { useState, useMemo, useRef, useCallback } from "react"
import { useSignUpUserMutation } from "@/services/User/UserMutations"
import queryClient from "@/services/QueryClient"
import { sendPasswordResetEmail } from "firebase/auth"
import { auth } from "@/firebase"
import { ReducedUserAdditionalInfo } from "@/models/User"
import { CSVLink } from "react-csv"
import { DateUtils } from "@/components/utils/DateUtils"

/**
* Component that handles all the network requests for `AdminMemberView`
Expand All @@ -35,6 +37,13 @@ const WrappedAdminMemberView = () => {
*/
const { mutateAsync: addNewUser } = useSignUpUserMutation("admin")

/**
* https://stackoverflow.com/a/68066447
*/
const csvLinkRef = useRef<
CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }
>(null)

/**
* @param email the email to be associated with the newly created user
* @param user the details to be appended to the user account. This should be the required fields for `UserAdditionalInfo`
Expand Down Expand Up @@ -78,24 +87,51 @@ const WrappedAdminMemberView = () => {
*/
const [showAddUserModal, setShowAddUserModal] = useState<boolean>(false)

/**
* Kept
*/
const untransformedUsers = useMemo(() => {
return (
data?.pages.flatMap(
(page) =>
page.data?.map((data) => {
return {
...data,
date_of_birth: DateUtils.formattedNzDate(
new Date(DateUtils.timestampMilliseconds(data.date_of_birth))
)
}
}) || [] // avoid undefined values in list
) || []
)
}, [data])

// Need flatmap because of inner map
const transformedDataList = data?.pages.flatMap(
(page) =>
page.data?.map((data) => {
const transformedDataList = useMemo(
() =>
untransformedUsers?.map((user) => {
const transformedData: MemberColumnFormat = { uid: "" }
transformedData.uid = data.uid
transformedData.Name = `${data.first_name} ${data.last_name}`
transformedData.Email = data.email
transformedData["Date Joined"] = data.dateJoined
transformedData.Status = data.membership
transformedData.uid = user.uid
transformedData.Name = `${user.first_name} ${user.last_name}`
transformedData.Email = user.email
transformedData["Date Joined"] = user.dateJoined
transformedData.Status = user.membership
return transformedData
}) || [] // avoid undefined values in list
}) || [], // avoid undefined values in list
[untransformedUsers]
)

const { mutateAsync: promoteUser } = usePromoteUserMutation()
const { mutateAsync: demoteUser } = useDemoteUserMutation()
const { mutateAsync: deleteUser, isPending } = useDeleteUserMutation()

const handleExportUsers = useCallback(() => {
if (hasNextPage) {
return
}
csvLinkRef.current?.link?.click()
}, [hasNextPage])

/**
* You should optimistically handle the mutations in `AdminMutations`
*/
Expand Down Expand Up @@ -144,10 +180,18 @@ const WrappedAdminMemberView = () => {

return (
<>
<CSVLink
className="hidden"
filename={"uasc-user-data.csv"}
data={untransformedUsers}
ref={csvLinkRef}
/>
<AdminMemberView
fetchNextPage={() => {
!isFetchingNextPage && hasNextPage && fetchNextPage()
}}
hasNextPage={hasNextPage}
exportUserDataHandler={handleExportUsers}
isUpdating={isPending}
rowOperations={rowOperations}
data={transformedDataList}
Expand Down
18 changes: 18 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10015,6 +10015,15 @@ __metadata:
languageName: node
linkType: hard

"@types/react-csv@npm:^1.1.10":
version: 1.1.10
resolution: "@types/react-csv@npm:1.1.10"
dependencies:
"@types/react": "npm:*"
checksum: 10c0/58d300aacccc70c67d2df538726983b4483f3b446db67687baa0026bd4d562cbb956a47eab49516bbe22b9bf8e9b92d1e28f608361a77b2ffa36825bd9c72295
languageName: node
linkType: hard

"@types/react-dom@npm:^18":
version: 18.3.0
resolution: "@types/react-dom@npm:18.3.0"
Expand Down Expand Up @@ -22375,6 +22384,13 @@ __metadata:
languageName: node
linkType: hard

"react-csv@npm:^2.2.2":
version: 2.2.2
resolution: "react-csv@npm:2.2.2"
checksum: 10c0/287e7ba2085a32a0e65d7a19b9e063e43f06f75ab6ebc18bb52b518007a1d6ec5f5ce33fc7c4e290e3ea41cfb251657b6e62aadadc8d7ffcef7e53d43bf3eb69
languageName: node
linkType: hard

"react-docgen-typescript@npm:^2.2.2":
version: 2.2.2
resolution: "react-docgen-typescript@npm:2.2.2"
Expand Down Expand Up @@ -25539,6 +25555,7 @@ __metadata:
"@types/node": "npm:^20.14.10"
"@types/prop-types": "npm:^15"
"@types/react": "npm:^18"
"@types/react-csv": "npm:^1.1.10"
"@types/react-dom": "npm:^18"
"@types/scheduler": "npm:^0"
eslint: "npm:~8.57.0"
Expand All @@ -25553,6 +25570,7 @@ __metadata:
prop-types: "npm:^15.8.1"
react: "npm:^18"
react-calendar: "npm:^5.0.0"
react-csv: "npm:^2.2.2"
react-dom: "npm:^18"
react-sweet-state: "npm:^2.7.1"
scheduler: "npm:^0.23.2"
Expand Down

0 comments on commit 9699b3b

Please sign in to comment.