From be1c8de5bf8059bef6b7f5b4cb422876404c5695 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Wed, 14 Aug 2024 19:50:21 +0800 Subject: [PATCH] Export user data --- client/package.json | 1 + client/src/icons.tsx | 19 +++++ client/src/pages/UsersManagementPage.tsx | 99 +++++++++++++++++++----- client/src/utilities.ts | 9 +++ server/routes/users.js | 22 +++++- 5 files changed, 125 insertions(+), 25 deletions(-) diff --git a/client/package.json b/client/package.json index 5090252..081b026 100644 --- a/client/package.json +++ b/client/package.json @@ -15,6 +15,7 @@ "axios": "^1.7.2", "chart.js": "^4.4.3", "dayjs": "^1.11.12", + "export-from-json": "^1.7.4", "formik": "^2.4.6", "framer-motion": "^11.2.10", "openai": "^4.53.2", diff --git a/client/src/icons.tsx b/client/src/icons.tsx index a1ec024..47d9cb3 100644 --- a/client/src/icons.tsx +++ b/client/src/icons.tsx @@ -748,3 +748,22 @@ export const ArrowTrendingUpIcon = () => { ); }; + +export const ArrowDownTrayIcon = () => { + return ( + + + + ); +}; diff --git a/client/src/pages/UsersManagementPage.tsx b/client/src/pages/UsersManagementPage.tsx index c56db1d..0a88dd4 100644 --- a/client/src/pages/UsersManagementPage.tsx +++ b/client/src/pages/UsersManagementPage.tsx @@ -9,18 +9,25 @@ import { Button, Tooltip, Input, + Dropdown, + DropdownTrigger, + DropdownMenu, + DropdownItem, + DropdownSection, } from "@nextui-org/react"; import { useEffect, useState } from "react"; import instance from "../security/http"; import config from "../config"; -import { popErrorToast, popToast } from "../utilities"; +import { exportData, popErrorToast, popToast } from "../utilities"; import { ArchiveBoxIcon, + ArrowDownTrayIcon, ClipboardDocumentIcon, LifebuoyIcon, MagnifyingGlassIcon, XMarkIcon, } from "../icons"; +import { ExportType } from "export-from-json"; export default function UsersManagement() { const [userInformationlist, setUserInformationList] = useState([]); @@ -110,33 +117,83 @@ export default function UsersManagement() { }); }; + const handleExport = (exportType: ExportType) => { + exportData( + userInformationlist, + `ecoconnect-user-data-${new Date().toUTCString()}`, + exportType + ); + }; + return (
{userInformationlist && (

Users Onboard

- } - endContent={ - searchQuery.length > 0 && ( - - ) - } - /> + + + + { + handleExport("json"); + }} + > + JavaScript Object Notation (JSON) + + { + handleExport("csv"); + }} + > + Comma-Separated Values (CSV) + + { + handleExport("xls"); + }} + > + Excel Workbook (XLS) + + + + + } + endContent={ + searchQuery.length > 0 && ( + + ) + } + /> +
diff --git a/client/src/utilities.ts b/client/src/utilities.ts index 42b8451..5c714f6 100644 --- a/client/src/utilities.ts +++ b/client/src/utilities.ts @@ -1,5 +1,6 @@ import { AxiosError } from "axios"; import toast from "react-hot-toast"; +import exportFromJSON, { ExportType } from "export-from-json"; export function getTimeOfDay(): number { const currentHour = new Date().getHours(); @@ -39,3 +40,11 @@ export const popErrorToast = (error: any) => { popToast("An unexpected error occured!\nPlease try again later.", 2); } }; + +export const exportData = ( + data: any, + fileName: string, + exportType: ExportType +) => { + exportFromJSON({ data, fileName, exportType }); +}; diff --git a/server/routes/users.js b/server/routes/users.js index 9213814..f93a968 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -90,14 +90,30 @@ router.get("/all", async (req, res) => { let list = await User.findAll({ where: condition, order: [["createdAt", "DESC"]], - attributes: { exclude: ["password", "profilePicture"] }, + attributes: { + exclude: [ + "password", + "profilePicture", + "resetPasswordToken", + "resetPasswordExpireTime", + ], + }, }); res.json(list); }); router.get("/individual/:id", validateToken, async (req, res) => { let id = req.params.id; - let user = await User.findByPk(id); + let user = await User.findByPk(id, { + attributes: { + exclude: [ + "password", + "profilePicture", + "resetPasswordToken", + "resetPasswordExpireTime", + ], + }, + }); if (!user) { res.sendStatus(404); @@ -109,8 +125,6 @@ router.get("/individual/:id", validateToken, async (req, res) => { message: `ERR_ACC_IS_ARCHIVED`, }); } else { - user.password = undefined; - user.profilePicture = undefined; res.json(user); } });