From 88e9108bf75beb8e76a2e28cff4c60afb83c3ad7 Mon Sep 17 00:00:00 2001 From: bconti123 Date: Mon, 17 Nov 2025 15:01:41 -0800 Subject: [PATCH 1/5] Add Export CSV button to project members UI. No CSV logic function yet. --- .../components/user-admin/UserPermissionSearch.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/components/user-admin/UserPermissionSearch.jsx b/client/src/components/user-admin/UserPermissionSearch.jsx index 96f1a950f..c967b0c0d 100644 --- a/client/src/components/user-admin/UserPermissionSearch.jsx +++ b/client/src/components/user-admin/UserPermissionSearch.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { Box, Button, @@ -249,7 +249,17 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { Project Members + + {isProjectLead && + + + + } Date: Mon, 17 Nov 2025 15:01:41 -0800 Subject: [PATCH 2/5] Add Export CSV button to project members UI. No CSV logic function yet. --- .../components/user-admin/UserPermissionSearch.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/components/user-admin/UserPermissionSearch.jsx b/client/src/components/user-admin/UserPermissionSearch.jsx index 96f1a950f..c967b0c0d 100644 --- a/client/src/components/user-admin/UserPermissionSearch.jsx +++ b/client/src/components/user-admin/UserPermissionSearch.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { Box, Button, @@ -249,7 +249,17 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { Project Members + + {isProjectLead && + + + + } Date: Wed, 26 Nov 2025 14:30:52 -0800 Subject: [PATCH 3/5] Implement data mapping for CSV export (no download logic yet) --- .../components/user-admin/UserPermissionSearch.jsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/src/components/user-admin/UserPermissionSearch.jsx b/client/src/components/user-admin/UserPermissionSearch.jsx index c967b0c0d..67ff10501 100644 --- a/client/src/components/user-admin/UserPermissionSearch.jsx +++ b/client/src/components/user-admin/UserPermissionSearch.jsx @@ -202,7 +202,20 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { // NOTE: Using "users" instead of "dummyData" to check the link to user profile filteredData = getFilteredData(resultData, searchText, isProjectLead); } + + // Export CSV function + const downloadCSVfile = (data) => { + // CSV header + const headers = ["name", "managedProjectName"]; + // Build rows + const rows = data.map((user) => [ + `${user.name?.firstName ?? ""} ${user.name?.lastName ??""}`, + user.managedProjectName ?? "" + ]); + console.log(rows); + } + downloadCSVfile(filteredData); return ( Date: Wed, 26 Nov 2025 22:59:22 -0800 Subject: [PATCH 4/5] Implement exportCSV logic and enable user/project CSV download --- .../user-admin/UserPermissionSearch.jsx | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/client/src/components/user-admin/UserPermissionSearch.jsx b/client/src/components/user-admin/UserPermissionSearch.jsx index 67ff10501..712dd3397 100644 --- a/client/src/components/user-admin/UserPermissionSearch.jsx +++ b/client/src/components/user-admin/UserPermissionSearch.jsx @@ -101,7 +101,7 @@ const DummyComponent = ({ data, isProjectLead, setUserToEdit }) => { }; const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { - const [userType, setUserType] = useState('admin'); // Which results will display + const [userType] = useState('admin'); // Which results will display const [searchText, setSearchText] = useState(''); // Search term for the admin/PM search const [isProjectLead, setIsProjectLead] = useState(false); @@ -136,7 +136,7 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { .filter((user) => isProjectLead ? user.isProjectLead === true - : user.isProjectLead === undefined + : user.isProjectLead === undefined, ) .flatMap((user) => isProjectLead && user.managedProjectNames.length > 0 @@ -144,7 +144,7 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { ...user, managedProjectName, })) - : [{ ...user }] + : [{ ...user }], ) .filter((user) => { const fullName = @@ -175,13 +175,13 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { filteredData = resultData.filter((user) => isProjectLead ? user.isProjectLead === true - : user.isProjectLead === undefined + : user.isProjectLead === undefined, ); if (!isProjectLead) { // Default display for admins, sorted ASC based on first name filteredData.sort((u1, u2) => - u1.name?.firstName.localeCompare(u2.name?.firstName) + u1.name?.firstName.localeCompare(u2.name?.firstName), ); } else { // Default display of all PMs, sorted ASC based on project name, then first name @@ -194,7 +194,7 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { tempFilter.sort( (u1, u2) => u1.managedProjectName.localeCompare(u2.managedProjectName) || - u1.name?.firstName.localeCompare(u2.name?.firstName) + u1.name?.firstName.localeCompare(u2.name?.firstName), ); filteredData = [...tempFilter]; } @@ -202,20 +202,40 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { // NOTE: Using "users" instead of "dummyData" to check the link to user profile filteredData = getFilteredData(resultData, searchText, isProjectLead); } - - // Export CSV function - const downloadCSVfile = (data) => { - // CSV header - const headers = ["name", "managedProjectName"]; - // Build rows - const rows = data.map((user) => [ - `${user.name?.firstName ?? ""} ${user.name?.lastName ??""}`, - user.managedProjectName ?? "" + // Export CSV file + const exportCSV = (data, fileName) => { + // Header row + const headers = ['User Name', 'Project Name']; + + // Map each user into CSV row values + const dataItems = data.map((user) => [ + `${user.name?.firstName ?? ''} ${user.name?.lastName ?? ''}`, + user.managedProjectName ?? '', ]); - console.log(rows); - } - downloadCSVfile(filteredData); + + // Combine header + data rows + const rows = [headers, ...dataItems]; + + // Convert rows to CSV formatted string + const csvData = rows.map((row) => row.join(',')).join('\r\n'); + + // Create CSV Blob from string + const blob = new Blob([csvData], { type: 'text/csv' }); + const url = URL.createObjectURL(blob); + + // Create a temporary download link + const link = document.createElement('a'); + link.href = url; + link.download = fileName; + + // Trigger download + link.click(); + + // Clean up object URL + URL.revokeObjectURL(url); + }; + return ( { Project Members - - {isProjectLead && - - - - } + {isProjectLead && ( + + + + )} Date: Fri, 28 Nov 2025 12:02:25 -0800 Subject: [PATCH 5/5] Implement exportAllData --- client/src/components/user-admin/UserPermissionSearch.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/components/user-admin/UserPermissionSearch.jsx b/client/src/components/user-admin/UserPermissionSearch.jsx index 712dd3397..e8a7e6b72 100644 --- a/client/src/components/user-admin/UserPermissionSearch.jsx +++ b/client/src/components/user-admin/UserPermissionSearch.jsx @@ -203,6 +203,9 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => { filteredData = getFilteredData(resultData, searchText, isProjectLead); } + // No need to limit export to the search results + const exportAllData = getFilteredData(resultData, '', isProjectLead); + // Export CSV file const exportCSV = (data, fileName) => { // Header row @@ -292,7 +295,7 @@ const UserPermissionSearch = ({ admins, projectLeads, setUserToEdit }) => {