From 0c0db322384312a3993c80152b47eee70f8182ec Mon Sep 17 00:00:00 2001 From: giostatic <116467840+giostatic@users.noreply.github.com> Date: Sat, 12 Apr 2025 20:24:08 -1000 Subject: [PATCH 1/2] Ability to update the request works, changes seem to be submitted to server based on local hosting, and added table UI to search results page --- techforgood/package-lock.json | 34 +++++ techforgood/package.json | 1 + techforgood/src/pages/request/Requests.js | 88 ++++++++++--- .../src/pages/request/UpdateRequestModal.js | 119 ++++++++++++++++++ .../src/pages/search/SearchRequests.js | 112 ++++++++++++----- 5 files changed, 304 insertions(+), 50 deletions(-) create mode 100644 techforgood/src/pages/request/UpdateRequestModal.js diff --git a/techforgood/package-lock.json b/techforgood/package-lock.json index 5d5a336..0a43fd8 100644 --- a/techforgood/package-lock.json +++ b/techforgood/package-lock.json @@ -14,6 +14,7 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", + "react-modal": "^3.16.3", "react-router-dom": "^7.1.5", "react-scripts": "5.0.1" }, @@ -7890,6 +7891,11 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -13655,6 +13661,26 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -16187,6 +16213,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", diff --git a/techforgood/package.json b/techforgood/package.json index 1e67ba2..9573e33 100644 --- a/techforgood/package.json +++ b/techforgood/package.json @@ -9,6 +9,7 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", + "react-modal": "^3.16.3", "react-router-dom": "^7.1.5", "react-scripts": "5.0.1" }, diff --git a/techforgood/src/pages/request/Requests.js b/techforgood/src/pages/request/Requests.js index 3223c7a..85c5cbb 100644 --- a/techforgood/src/pages/request/Requests.js +++ b/techforgood/src/pages/request/Requests.js @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import getRequests from '../../features/firebase/auth/getRequests'; import deleteRequest from '../../features/firebase/auth/deleteRequest'; import { useAuth } from '../../features/firebase/AuthProvider'; +import UpdateRequestModal from './UpdateRequestModal'; // Import the modal component const UserRequests = () => { const { user, loading: authLoading } = useAuth(); @@ -10,6 +11,10 @@ const UserRequests = () => { const [error, setError] = useState(null); const [currentPage, setCurrentPage] = useState(1); const resultsPerPage = 5; + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedRequest, setSelectedRequest] = useState(null); + const [successMessage, setSuccessMessage] = useState(''); // State for success message + const [fadeOut, setFadeOut] = useState(false); // State for fade-out effect useEffect(() => { const fetchRequests = async () => { @@ -45,6 +50,16 @@ const UserRequests = () => { } }; + const handleEdit = (request) => { + setSelectedRequest(request); + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + setSelectedRequest(null); + }; + const handleNextPage = () => { setCurrentPage(prevPage => prevPage + 1); }; @@ -64,30 +79,52 @@ const UserRequests = () => { return (

Requests your or your org have made

+ {successMessage && ( +

+ {successMessage} +

+ )} + - {/* looking in to createPortal. Without using react strap, we can create a modal to open to - might be better to display lengthy information like request descrition this way */} - {currentResults.map((request) => ( - - - - - - - ))} + {currentResults.map((request) => ( + + + + + + + + ))}
Title DescriptionKeywords Date Created Actions
{request.title}{request.description}{new Date(request.created).toLocaleDateString()} - - {/* onClick={() => handleEdit(request.id)} */} - -
{request.title} + {request.description.length > 100 ? ( + handleEdit(request)} + > + {request.description.substring(0, 100)}... + + ) : ( + request.description + )} + {request.keywords ? request.keywords.join(', ') : 'N/A'}{new Date(request.created).toLocaleDateString()} + + +
@@ -95,6 +132,26 @@ const UserRequests = () => { {currentPage > 1 && } {indexOfLastResult < requests.length && }
+ {isModalOpen && ( + { + setRequests((prevRequests) => + prevRequests.map((request) => + request.id === updatedRequest.id ? updatedRequest : request + ) + ); + setSuccessMessage('Your request has been updated'); // Set success message + console.log('Request updated successfully'); // Log message to console + setFadeOut(false); // Reset fade-out state + setTimeout(() => setFadeOut(true), 9000); // Start fade-out after 9 seconds + setTimeout(() => setSuccessMessage(''), 10000); // Clear the message after 10 seconds + closeModal(); + }} + /> + )}
); }; @@ -122,8 +179,7 @@ const styles = { fontWeight: 'bold', }, actionsStyling: { - display: 'flex', - justifyContent: 'center', + gap: '5px', padding: '5px', }, diff --git a/techforgood/src/pages/request/UpdateRequestModal.js b/techforgood/src/pages/request/UpdateRequestModal.js new file mode 100644 index 0000000..f87704e --- /dev/null +++ b/techforgood/src/pages/request/UpdateRequestModal.js @@ -0,0 +1,119 @@ +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { doc, updateDoc, getFirestore } from 'firebase/firestore'; // Import Firestore methods + + +const UpdateRequestModal = ({ isOpen, onRequestClose, modalContent, onSubmit }) => { + const { register, handleSubmit, reset } = useForm(); + + useEffect(() => { + if (isOpen && modalContent) { // Ensure modalContent is valid and modal is open + reset({ + title: modalContent.title || '', + description: modalContent.description || '', + keywords: modalContent.keywords?.join(', ') || '', // Convert keywords array to comma-separated string + }); + } + }, [modalContent, isOpen, reset]); + + const onSubmitForm = async (data) => { + try { + const updatedRequest = { + ...modalContent, + ...data, + keywords: data.keywords.split(',').map((kw) => kw.trim()), // Convert back to array + }; + + // Update the document in the requests collection + const database = getFirestore(); // Use getFirestore() to initialize Firestore + const requestDocRef = doc(database, 'requests', modalContent.id); // Reference to the document + await updateDoc(requestDocRef, updatedRequest); // Update the document in Firestore + + onSubmit(updatedRequest); // Pass the updated request back to the parent + } catch (error) { + console.error('Error updating request:', error); + } + }; + + if (!isOpen) return null; + + return ( +
+
+

Update Request

+
+
+ + +
+
+ +