Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions backend/controllers/project.controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { Project, User } = require('../models');
const { ObjectId } = require('mongodb');

const ProjectController = {};

Expand Down Expand Up @@ -87,11 +88,11 @@ ProjectController.updateManagedByUsers = async function (req, res) {
managedProjects = managedProjects.filter((id) => id !== ProjectId);
}

// Update project's managedByUsers
// Update project's managedByUsers
project.managedByUsers = managedByUsers;
await project.save({ validateBeforeSave: false });

// Update user's managedProjects
// Update user's managedProjects
user.managedProjects = managedProjects;
await user.save({ validateBeforeSave: false });

Expand All @@ -102,4 +103,31 @@ ProjectController.updateManagedByUsers = async function (req, res) {
}
};

ProjectController.bulkUpdateManagedByUsers = async function (req, res) {
const { bulkOps } = req.body;

// Convert string IDs to ObjectId in bulkOps
bulkOps.forEach((op) => {
if (op?.updateOne?.filter._id) {
op.updateOne.filter._id = ObjectId(op.updateOne.filter._id);
}
if (op?.updateOne?.update) {
const update = op.updateOne.update;
if (update?.$addToSet?.managedByUsers) {
update.$addToSet.managedByUsers = ObjectId(update.$addToSet.managedByUsers);
}
if (update?.$pull?.managedByUsers) {
update.$pull.managedByUsers = ObjectId(update.$pull.managedByUsers);
}
}
});

try {
const result = await Project.bulkWrite(bulkOps);
res.status(200).json(result);
} catch (err) {
res.status(500).json({ error: err.message });
}
};

module.exports = ProjectController;
51 changes: 48 additions & 3 deletions backend/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const jwt = require('jsonwebtoken');
const { ObjectId } = require('mongodb');

const EmailController = require('./email.controller');
const { CONFIG_AUTH } = require('../config');
Expand Down Expand Up @@ -27,6 +28,25 @@ UserController.user_list = async function (req, res) {
}
};

UserController.user_by_email = async function (req, res) {
const { headers } = req;
const { email } = req.params;

console.log('email: ', email);

if (headers['x-customrequired-header'] !== expectedHeader) {
return res.sendStatus(403);
}

try {
const user = await User.find({ email });
return res.status(200).send(user);
} catch (err) {
console.log(err);
return res.sendStatus(400);
}
};

// Get list of Users with accessLevel 'admin' or 'superadmin' with GET
UserController.admin_list = async function (req, res) {
const { headers } = req;
Expand Down Expand Up @@ -104,8 +124,6 @@ UserController.user_by_id = async function (req, res) {

try {
const user = await User.findById(UserId);
// TODO throw 404 if User.findById returns empty object
// and look downstream to see whether 404 would break anything
return res.status(200).send(user);
} catch (err) {
console.error(err);
Expand Down Expand Up @@ -294,7 +312,7 @@ UserController.updateManagedProjects = async function (req, res) {
managedByUsers = managedByUsers.filter((id) => id !== UserId);
}

// Update user's managedProjects
// Update user's managedProjects
user.managedProjects = managedProjects;
await user.save({ validateBeforeSave: false });

Expand All @@ -309,4 +327,31 @@ UserController.updateManagedProjects = async function (req, res) {
}
};

UserController.bulkUpdateManagedProjects = async function (req, res) {
const { bulkOps } = req.body;

// Convert string IDs to ObjectId in bulkOps
bulkOps.forEach((op) => {
if (op?.updateOne?.filter._id) {
op.updateOne.filter._id = ObjectId(op.updateOne.filter._id);
}
if (op?.updateOne?.update) {
const update = op.updateOne.update;
if (update?.$addToSet?.managedProjects) {
update.$addToSet.managedProjects = ObjectId(update.$addToSet.managedProjects);
}
if (update?.$pull?.managedProjects) {
update.$pull.managedProjects = ObjectId(update.$pull.managedProjects);
}
}
});

try {
const result = await User.bulkWrite(bulkOps);
res.status(200).json(result);
} catch (err) {
res.status(500).json({ error: err.message });
}
};

module.exports = UserController;
2 changes: 1 addition & 1 deletion backend/models/user.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const userSchema = mongoose.Schema({
isHflaGithubMember: { type: Boolean }, // pull from API once github handle in place?
githubPublic2FA: { type: Boolean }, // does the user have 2FA enabled on their github and membership set to public?
availability: { type: String }, // availability to meet outside of hacknight times; string for now, more structured in future
managedProjects: [{ type: String}], // Which projects managed by user.
managedProjects: [{ type: String }], // Which projects managed by user.
//currentProject: { type: String } // no longer need this as we can get it from Project Team Member table
// password: { type: String, required: true }
isActive: { type: Boolean, default: true }
Expand Down
5 changes: 4 additions & 1 deletion backend/routers/projects.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ router.put('/', ProjectController.pm_filtered_projects);

router.post('/', AuthUtil.verifyCookie, ProjectController.create);

router.get('/:ProjectId', ProjectController.project_by_id);
router.get('/:ProjectId', AuthUtil.verifyCookie, ProjectController.project_by_id);

router.put('/:ProjectId', AuthUtil.verifyCookie, ProjectController.update);

// Update project's managedByUsers in db
router.patch('/:ProjectId', AuthUtil.verifyCookie, ProjectController.updateManagedByUsers);

// Bulk update for editing project members
router.post('/bulk-updates', AuthUtil.verifyCookie, ProjectController.bulkUpdateManagedByUsers);

module.exports = router;
7 changes: 5 additions & 2 deletions backend/routers/users.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ const { UserController } = require('../controllers');
// The base is /api/users
router.get('/', UserController.user_list);

router.get('/id/:UserId', UserController.user_by_id);

router.get('/email/:email', UserController.user_by_email);

router.get('/admins', UserController.admin_list);

router.get('/projectManagers', UserController.projectManager_list);

router.post('/', UserController.create);

router.get('/:UserId', UserController.user_by_id);
router.post('/bulk-updates', UserController.bulkUpdateManagedProjects);

router.patch('/:UserId', UserController.update);

// Update user projects in db
router.patch('/:UserId/managedProjects', UserController.updateManagedProjects);

router.delete('/:UserId', UserController.delete);
Expand Down
29 changes: 29 additions & 0 deletions client/src/api/ProjectApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,35 @@ class ProjectApiService {
return undefined;
}
}

async fetchManagedByUsers(projectId) {
const url = `${this.baseProjectUrl}${projectId}`;
try {
const res = await fetch(url, {
headers: this.headers,
method: 'GET',
});
return await res.json();
} catch (error) {
console.error(`fetchManagedByUsers error: ${error}`);
alert('Server not responding. Please refresh the page.');
}
}

async bulkUpdateManagedByUsers(bulkOps) {
const url = `${this.baseProjectUrl}bulk-updates`;
try {
const res = await fetch(url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({ bulkOps }),
});
return await res.json();
} catch (error) {
console.error(`bulkUpdateManagedByUsers error: ${error}`);
alert('Server not responding. Please refresh the page.');
}
}
}

export default ProjectApiService;
45 changes: 45 additions & 0 deletions client/src/api/UserApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@ class UserApiService {
const res = await fetch(this.baseUserUrl, {
headers: this.headers,
});
console.log(res);
return await res.json();
} catch (error) {
console.error(`fetchUsers error: ${error}`);
alert('Server not responding. Please refresh the page.');
}
return [];
}

async fetchUserById(id) {
try {
const uri = `${this.baseUserUrl}id/${id}`;
const res = await fetch(uri, {
headers: this.headers,
});
return await res.json();
} catch (error) {
console.error(`fetchUsers error: ${error}`);
alert('Server not responding. Please refresh the page.');
}
return [];
}

// Fetch user by email
async fetchUserByEmail(email) {
try {
const uri = `${this.baseUserUrl}email/${email}`;
const res = await fetch(uri, {
headers: this.headers,
});
return await res.json();
} catch (error) {
console.error(`fetchUsers error: ${error}`);
Expand Down Expand Up @@ -118,6 +148,21 @@ class UserApiService {
alert('server not responding. Please try again.');
}
}

async bulkUpdateManagedProjects(bulkOps) {
const url = `${this.baseUserUrl}bulk-updates`;
try {
const res = await fetch(url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({ bulkOps }),
});
return await res.json();
} catch (error) {
console.error(`bulkUpdateManagedProjects error: ${error}`);
alert('Server not responding. Please try again.');
}
}
}

export default UserApiService;
29 changes: 29 additions & 0 deletions client/src/components/manageProjects/editPMs/buttonGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CircularProgress, Grid } from "@mui/material";
import { StyledButton } from '../../ProjectForm';

const ButtonGroup = ({ btnName1, btnName2, callBackFn1, callBackFn2, isLoading }) => (
<Grid container justifyContent="space-evenly" sx={{ my: 3 }}>
<Grid item xs="auto">
<StyledButton
sx="large"
cursor="pointer"
variant="contained"
onClick={(btn) => callBackFn1(btn)}
>
{isLoading ? <CircularProgress /> : `${btnName1}`}
</StyledButton>
</Grid>
<Grid item xs="auto">
<StyledButton
sx="large"
cursor="pointer"
variant="contained"
onClick={callBackFn2}
>
{btnName2}
</StyledButton>
</Grid>
</Grid>
);

export default ButtonGroup
Loading
Loading