diff --git a/techforgood/build/asset-manifest.json b/techforgood/build/asset-manifest.json index 9c2d4c7..82c9cec 100644 --- a/techforgood/build/asset-manifest.json +++ b/techforgood/build/asset-manifest.json @@ -1,13 +1,13 @@ { "files": { "main.css": "/static/css/main.94d3a9b7.css", - "main.js": "/static/js/main.8d564318.js", + "main.js": "/static/js/main.c4fc4305.js", "index.html": "/index.html", "main.94d3a9b7.css.map": "/static/css/main.94d3a9b7.css.map", - "main.8d564318.js.map": "/static/js/main.8d564318.js.map" + "main.c4fc4305.js.map": "/static/js/main.c4fc4305.js.map" }, "entrypoints": [ "static/css/main.94d3a9b7.css", - "static/js/main.8d564318.js" + "static/js/main.c4fc4305.js" ] } \ No newline at end of file diff --git a/techforgood/build/index.html b/techforgood/build/index.html index 97bc208..5f34c7a 100644 --- a/techforgood/build/index.html +++ b/techforgood/build/index.html @@ -1 +1 @@ -React App
\ No newline at end of file +React App
\ No newline at end of file diff --git a/techforgood/build/static/css/main.94d3a9b7.css.map b/techforgood/build/static/css/main.94d3a9b7.css.map index 18eaa78..af14934 100644 --- a/techforgood/build/static/css/main.94d3a9b7.css.map +++ b/techforgood/build/static/css/main.94d3a9b7.css.map @@ -1 +1 @@ -{"version":3,"file":"static/css/main.94d3a9b7.css","mappings":"AAAA,KAKE,kCAAmC,CACnC,iCAAkC,CAJlC,mIAEY,CAHZ,QAMF,CAEA,KACE,uEAEF,CCZA,KACE,iBACF,CAEA,UACE,aAAc,CACd,mBACF,CAEA,8CACE,UACE,2CACF,CACF,CAEA,YAKE,kBAAmB,CAJnB,wBAAyB,CAOzB,UAAY,CALZ,YAAa,CACb,qBAAsB,CAGtB,4BAA6B,CAD7B,sBAAuB,CAJvB,gBAOF,CAEA,UACE,aACF,CAEA,yBACE,GACE,sBACF,CACA,GACE,uBACF,CACF,CAEA,aAGE,kBAAmB,CADnB,qBAGF,CAOA,6CAXE,YAAa,CAGb,OAaF,CALA,aAGE,kBAAmB,CADnB,qBAGF,CAEA,aACE,aACF","sources":["index.css","App.css"],"sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n",".App {\r\n text-align: center;\r\n}\r\n\r\n.App-logo {\r\n height: 40vmin;\r\n pointer-events: none;\r\n}\r\n\r\n@media (prefers-reduced-motion: no-preference) {\r\n .App-logo {\r\n animation: App-logo-spin infinite 20s linear;\r\n }\r\n}\r\n\r\n.App-header {\r\n background-color: #282c34;\r\n min-height: 100vh;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: calc(10px + 2vmin);\r\n color: white;\r\n}\r\n\r\n.App-link {\r\n color: #61dafb;\r\n}\r\n\r\n@keyframes App-logo-spin {\r\n from {\r\n transform: rotate(0deg);\r\n }\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n.userDetails{\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 1em;\r\n}\r\n\r\n.userDetails label{\r\n display: flex;\r\n gap: 1em;\r\n}\r\n\r\n.formWrapper{\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 1em;\r\n}\r\n\r\n.userOptions{\r\n max-width: 50%;\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"static/css/main.94d3a9b7.css","mappings":"AAAA,KAKE,kCAAmC,CACnC,iCAAkC,CAJlC,mIAEY,CAHZ,QAMF,CAEA,KACE,uEAEF,CCZA,KACE,iBACF,CAEA,UACE,aAAc,CACd,mBACF,CAEA,8CACE,UACE,2CACF,CACF,CAEA,YAKE,kBAAmB,CAJnB,wBAAyB,CAOzB,UAAY,CALZ,YAAa,CACb,qBAAsB,CAGtB,4BAA6B,CAD7B,sBAAuB,CAJvB,gBAOF,CAEA,UACE,aACF,CAEA,yBACE,GACE,sBACF,CACA,GACE,uBACF,CACF,CAEA,aAGE,kBAAmB,CADnB,qBAGF,CAOA,6CAXE,YAAa,CAGb,OAaF,CALA,aAGE,kBAAmB,CADnB,qBAGF,CAEA,aACE,aACF","sources":["index.css","App.css"],"sourcesContent":["body {\r\n margin: 0;\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\r\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\r\n sans-serif;\r\n -webkit-font-smoothing: antialiased;\r\n -moz-osx-font-smoothing: grayscale;\r\n}\r\n\r\ncode {\r\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\r\n monospace;\r\n}\r\n",".App {\r\n text-align: center;\r\n}\r\n\r\n.App-logo {\r\n height: 40vmin;\r\n pointer-events: none;\r\n}\r\n\r\n@media (prefers-reduced-motion: no-preference) {\r\n .App-logo {\r\n animation: App-logo-spin infinite 20s linear;\r\n }\r\n}\r\n\r\n.App-header {\r\n background-color: #282c34;\r\n min-height: 100vh;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: calc(10px + 2vmin);\r\n color: white;\r\n}\r\n\r\n.App-link {\r\n color: #61dafb;\r\n}\r\n\r\n@keyframes App-logo-spin {\r\n from {\r\n transform: rotate(0deg);\r\n }\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n.userDetails{\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 1em;\r\n}\r\n\r\n.userDetails label{\r\n display: flex;\r\n gap: 1em;\r\n}\r\n\r\n.formWrapper{\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 1em;\r\n}\r\n\r\n.userOptions{\r\n max-width: 50%;\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/techforgood/package-lock.json b/techforgood/package-lock.json index b932a73..aefec29 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", @@ -13642,6 +13648,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", @@ -16174,6 +16200,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

+
+
+ + +
+
+ +