diff --git a/backend/routers/projects.router.js b/backend/routers/projects.router.js index 8991000cb..385d1229a 100644 --- a/backend/routers/projects.router.js +++ b/backend/routers/projects.router.js @@ -1,4 +1,4 @@ -const express = require("express"); +const express = require('express'); const router = express.Router(); const { ProjectController } = require('../controllers'); @@ -10,7 +10,8 @@ router.post('/', ProjectController.create); router.get('/:ProjectId', ProjectController.project_by_id); -router.patch('/:ProjectId', ProjectController.update); +router.put('/:ProjectId', ProjectController.update); +router.patch('/:ProjectId', ProjectController.update); module.exports = router; diff --git a/backend/routers/projects.router.test.js b/backend/routers/projects.router.test.js index 5233247b9..36e16c708 100644 --- a/backend/routers/projects.router.test.js +++ b/backend/routers/projects.router.test.js @@ -20,10 +20,7 @@ describe('CREATE', () => { }; // Submit a project - const res = await request - .post('/api/projects/') - .set(headers) - .send(submittedData); + const res = await request.post('/api/projects/').set(headers).send(submittedData); expect(res.status).toBe(201); done(); }); @@ -31,26 +28,23 @@ describe('CREATE', () => { describe('READ', () => { test('Get all projects with GET to /api/projects/', async (done) => { - // Test Data - const submittedData = { - name: 'projectName', - }; - - // Submit a project - const res = await request - .post('/api/projects/') - .set(headers) - .send(submittedData); - expect(res.status).toBe(201); - - // Get all projects - const res2 = await request.get('/api/projects/').set(headers); - expect(res2.status).toBe(200); - - const APIData = res2.body[0]; - expect(APIData.name).toBe(submittedData.name); - done(); - });; + // Test Data + const submittedData = { + name: 'projectName', + }; + + // Submit a project + const res = await request.post('/api/projects/').set(headers).send(submittedData); + expect(res.status).toBe(201); + + // Get all projects + const res2 = await request.get('/api/projects/').set(headers); + expect(res2.status).toBe(200); + + const APIData = res2.body[0]; + expect(APIData.name).toBe(submittedData.name); + done(); + }); }); describe('UPDATE', () => { @@ -61,10 +55,7 @@ describe('UPDATE', () => { }; // Submit a project - const res = await request - .post('/api/projects/') - .set(headers) - .send(submittedData); + const res = await request.post('/api/projects/').set(headers).send(submittedData); expect(res.status).toBe(201); const updatedDataPayload = { @@ -73,7 +64,7 @@ describe('UPDATE', () => { // Update project const res2 = await request - .patch(`/api/projects/${res.body._id}`) + .put(`/api/projects/${res.body._id}`) .set(headers) .send(updatedDataPayload); expect(res2.status).toBe(200); @@ -96,15 +87,12 @@ describe('DELETE', () => { }; // Submit a project - const res = await request - .post('/api/projects/') - .set(headers) - .send(submittedData); + const res = await request.post('/api/projects/').set(headers).send(submittedData); expect(res.status).toBe(201); // Delete project const res2 = await request.patch(`/api/projects/${res.body._id}`).set(headers); expect(res2.status).toBe(200); done(); -}); + }); }); diff --git a/client/src/api/ProjectApiService.js b/client/src/api/ProjectApiService.js index b97e88cf2..b43b5cdbd 100644 --- a/client/src/api/ProjectApiService.js +++ b/client/src/api/ProjectApiService.js @@ -52,9 +52,9 @@ class ProjectApiService { console.log('THIS BASEPROJECT URL', this.baseProjectUrl); try { - const proj = await fetch(this.baseProjectUrl, requestOptions); - const projectDetails = await proj.json() - return projectDetails._id + const proj = await fetch(this.baseProjectUrl, requestOptions); + const projectDetails = await proj.json(); + return projectDetails._id; } catch (error) { console.error(`Add project error: `, error); alert('Server not responding. Please try again.'); @@ -62,32 +62,18 @@ class ProjectApiService { } } - async updateProject(projectId, fieldName, fieldValue) { - let updateValue = fieldValue; - // These field are arrays, but the form makes them comma separated strings, - // so this adds it back to db as an arrray. - if ( - fieldValue && - (fieldName === 'partners' || fieldName === 'recruitingCategories') - ) { - updateValue = fieldValue - .split(',') - .filter((x) => x !== '') - .map((y) => y.trim()); - } - + async updateProject(projectId, projectData) { // Update database const url = `${this.baseProjectUrl}${projectId}`; const requestOptions = { - method: 'PATCH', + method: 'PUT', headers: this.headers, - body: JSON.stringify({ [fieldName]: updateValue }), + body: JSON.stringify({ ...projectData }), }; try { const response = await fetch(url, requestOptions); const resJson = await response.json(); - return resJson; } catch (error) { console.log(`update project error: `, error); diff --git a/client/src/components/manageProjects/editProject.js b/client/src/components/manageProjects/editProject.js index 21d7f5798..a4af87481 100644 --- a/client/src/components/manageProjects/editProject.js +++ b/client/src/components/manageProjects/editProject.js @@ -1,11 +1,44 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; -import EditableField from './editableField'; import EditMeetingTimes from './editMeetingTimes'; import CreateNewEvent from './createNewEvent'; import readableEvent from './utilities/readableEvent'; -import { ReactComponent as EditIcon } from "../../svg/Icon_Edit.svg"; -import '../../sass/ManageProjects.scss'; +import ProjectApiService from '../../api/ProjectApiService'; + +import { ReactComponent as EditIcon } from '../../svg/Icon_Edit.svg'; +import { ReactComponent as PlusIcon } from '../../svg/PlusIcon.svg'; + +import { + Typography, + Box, + Divider, + TextField, + InputLabel, + Button, + Grid, + Radio, + FormControl, + FormControlLabel, + RadioGroup, +} from '@mui/material'; + +import { styled } from '@mui/material/styles'; + +const StyledButton = styled(Button)(({ theme }) => ({ + width: '150px', +})); + +const StyledFormControlLabel = styled(FormControlLabel)(({ theme }) => ({ + width: 'max-content', + '& .MuiFormControlLabel-label': { + fontSize: '14px', + }, +})); + +const StyledRadio = styled(Radio)(({ theme }) => ({ + padding: '0px 0px 0px 0px', + marginRight: '.5rem', +})); // Need to hold user state to check which type of user they are and conditionally render editing fields in this component // for user level block access to all except for the ones checked @@ -18,10 +51,28 @@ const EditProject = ({ deleteRecurringEvent, updateRecurringEvent, }) => { - // Add commas to arrays for display - const partnerDataFormatted = projectToEdit.partners.join(', '); + const [formData, setFormData] = useState({ + name: projectToEdit.name, + description: projectToEdit.description, + location: projectToEdit.location, + // githubIdentifier: projectToEdit.name, + githubUrl: projectToEdit.githubUrl, + slackUrl: projectToEdit.slackUrl, + googleDriveUrl: projectToEdit.googleDriveUrl, + // hflaWebsiteUrl: projectToEdit.name, + }); + const [locationType, setLocationType] = React.useState('remote'); + // Set up to hold state for location type when added to add projects. + //********** */ + const [selected, setSelected] = useState(''); + const isButtonSelected = (value) => { + if (selected === value) { + return true; + } + }; + //********** */ // eslint-disable-next-line no-unused-vars - const recrutingDataFormatted = projectToEdit.recruitingCategories.join(', '); + const [rEvents, setREvents] = useState([]); const [selectedEvent, setSelectedEvent] = useState(); const [isCreateNew, setIsCreateNew] = useState(); @@ -29,6 +80,63 @@ const EditProject = ({ // States for alerts const [eventAlert, setEventAlert] = useState(null); + // Holds active state for close/save buttons + const [activeButton, setActiveButton] = React.useState('close'); + + // Form inputs. + const simpleInputs = [ + { + label: 'Project Name', + name: 'name', + type: 'text', + value: projectToEdit.name, + }, + { + label: 'Project Description', + name: 'description', + type: 'textarea', + value: projectToEdit.description, + }, + { + label: 'Location', + name: 'location', + type: 'text', + value: projectToEdit.location, + }, + // Leaving incase we want to add this back in for updating projects + // { + // label: 'GitHub Identifier', + // name: 'githubIdentifier', + // type: 'text', + // placeholder: 'Enter GitHub identifier', + // }, + { + label: 'GitHub URL', + name: 'githubUrl', + type: 'text', + value: projectToEdit.githubUrl, + }, + { + label: 'Slack Channel Link', + name: 'slackUrl', + type: 'text', + value: projectToEdit.slackUrl, + }, + { + label: 'Google Drive URL', + name: 'googleDriveUrl', + type: 'text', + value: projectToEdit.googleDriveUrl, + }, + // Leaving incase we want to add this back in for updating projects + // { + // label: 'HFLA Website URL', + // name: 'hflaWebsiteUrl', + // type: 'text', + // value: projectToEdit.hflaWebsiteUrl, + // }, + ]; + // Get project recurring events when component loads useEffect(() => { if (recurringEvents) { @@ -42,8 +150,76 @@ const EditProject = ({ } }, [projectToEdit, recurringEvents, setREvents]); + // only handles radio button change + const handleRadioChange = (event) => { + setLocationType(event.target.value); + }; + + // updates state of formData onChange of any form input + const handleChange = (e) => { + const { name, value } = e.target; + + setFormData((fData) => ({ + ...fData, + [name]: value, + })); + }; + + // fires PUT request to update the project, + + const handleSubmit = async (e) => { + e.preventDefault(); + const projectApi = new ProjectApiService(); + try { + await projectApi.updateProject(projectToEdit._id, formData); + } catch (errors) { + console.error(errors); + return; + } + setActiveButton('close'); + }; + + // Basic validation : if all inputs have values, enable the submit button + useEffect(() => { + if (Object.values(formData).every((val) => val !== '')) { + setActiveButton('save'); + } else { + setActiveButton('close'); + } + }, [formData]); + + // Displays the location radios if the input.type === 'location'. See below. + const locationRadios = ( + + + + } + label="Remote" + /> + + } + label="In-Person" + /> + + + + ); + return ( -
+
- - All Projects - -
- *The data here is only test data and is not accurate* -
-
{`Project: ${projectToEdit.name}`}
- - - - - - - - {/* */} - - {/* */} - {/* */} - - {/* */} -
-

Recurring Events

-

{eventAlert}

-
    - {rEvents.map((event) => ( - // eslint-disable-next-line no-underscore-dangle -
  • - -
  • - ))} -
-
-
- -
-
+ + + Recurring Events + + + setIsCreateNew(true)} + > + + + Add New Event + + + + + +
+

{eventAlert}

+
    + {rEvents.map((event) => ( + // eslint-disable-next-line no-underscore-dangle +
  • + +
  • + ))} +
+
+
+
+ + + + + + + + Save + + + + + Close + + + + + ); }; diff --git a/client/src/sass/ManageProjects.scss b/client/src/sass/ManageProjects.scss index 67292c383..49dbd06ac 100644 --- a/client/src/sass/ManageProjects.scss +++ b/client/src/sass/ManageProjects.scss @@ -139,7 +139,6 @@ div.editable-field { ul { padding-top: 4px; - border-top: 2px solid #32373b; } li { @@ -313,7 +312,7 @@ textarea { } .event-list-details { - display: flex; + display: flex; } .edit-icon {