Skip to content

Commit ef315e4

Browse files
authored
Merge branch 'development' into fix/recurring-checkin-bulk
2 parents 88603dd + df26aff commit ef315e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+4287
-1226
lines changed

.github/workflows/pr-instructions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
echo ${{ steps.instruction.outputs.result }} > addingPrInstructions/artifact/artifact.txt
2929
3030
- name: Upload Artifacts
31-
uses: actions/upload-artifact@v3
31+
uses: actions/upload-artifact@v4
3232
with:
3333
name: adding-pr-instructions-artifact
3434
path: addingPrInstructions/artifact/

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
# vscode dirs
2+
.devcontainer
3+
.vscode
14

5+
# project dirs and files
26
node_modules/
37
npm-debug.log
48
.DS_Store

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ Note: Understanding how git remotes work will make collaborating much easier. Yo
106106

107107
### **2.3 Get up and running**
108108

109+
1. Install [NVM (Node Version Manager)](https://github.com/nvm-sh/nvm). NVM allows you to easily manage and switch between multiple versions of Node.
110+
111+
- Verify the installation: `nvm --version`
112+
- Once NVM is verified, run the following commands from the root of the project:
113+
114+
```
115+
# Install the project's Node version (specified in the .nvmrc file)
116+
nvm install
117+
118+
# Instruct NVM to use the Node version defined in the .nvmrc file
119+
nvm use
120+
121+
```
122+
123+
> NOTE: If the major version of Node does not match the version specified in the .nvmrc file, you may need to be explicit with the version and use: `nvm install <version>` and `nvm use <version>`
124+
109125
1. Have [Node](https://nodejs.org/en/download/) and NPM installed locally:
110126

111127
- Verify with `node -v` and `npm -v` respectively.

VRMS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 7042af0f43f0b034977c7f64c613e9d8b7199edb

backend/controllers/project.controller.js

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { Project, User } = require('../models');
2+
const { ObjectId } = require('mongodb');
23

34
const ProjectController = {};
45

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

90-
// Update project's managedByUsers
91+
// Update project's managedByUsers
9192
project.managedByUsers = managedByUsers;
9293
await project.save({ validateBeforeSave: false });
9394

94-
// Update user's managedProjects
95+
// Update user's managedProjects
9596
user.managedProjects = managedProjects;
9697
await user.save({ validateBeforeSave: false });
9798

@@ -102,4 +103,53 @@ ProjectController.updateManagedByUsers = async function (req, res) {
102103
}
103104
};
104105

106+
ProjectController.bulkUpdateManagedByUsers = async function (req, res) {
107+
const { bulkOps } = req.body;
108+
109+
// Convert string IDs to ObjectId in bulkOps
110+
bulkOps.forEach((op) => {
111+
if (op?.updateOne?.filter._id) {
112+
op.updateOne.filter._id = ObjectId(op.updateOne.filter._id);
113+
}
114+
if (op?.updateOne?.update) {
115+
const update = op.updateOne.update;
116+
if (update?.$addToSet?.managedByUsers) {
117+
update.$addToSet.managedByUsers = ObjectId(update.$addToSet.managedByUsers);
118+
}
119+
if (update?.$pull?.managedByUsers) {
120+
update.$pull.managedByUsers = ObjectId(update.$pull.managedByUsers);
121+
}
122+
}
123+
});
124+
125+
try {
126+
const result = await Project.bulkWrite(bulkOps);
127+
res.status(200).json(result);
128+
} catch (err) {
129+
res.status(500).json({ error: err.message });
130+
}
131+
};
132+
133+
// Update onboard/offboard visibility for a project
134+
ProjectController.updateOnboardOffboardVisibility = async function (req, res) {
135+
const { ProjectId } = req.params;
136+
const { onboardOffboardVisible } = req.body;
137+
138+
try {
139+
const project = await Project.findByIdAndUpdate(
140+
ProjectId,
141+
{ onboardOffboardVisible },
142+
{ new: true }
143+
);
144+
145+
if (!project) {
146+
return res.status(404).send({ message: 'Project not found' });
147+
}
148+
149+
return res.status(200).send(project);
150+
} catch (err) {
151+
return res.status(400).send({ message: 'Error updating visibility', error: err.message });
152+
}
153+
};
154+
105155
module.exports = ProjectController;

backend/controllers/user.controller.js

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const jwt = require('jsonwebtoken');
2+
const { ObjectId } = require('mongodb');
23

34
const EmailController = require('./email.controller');
45
const { CONFIG_AUTH } = require('../config');
@@ -27,6 +28,25 @@ UserController.user_list = async function (req, res) {
2728
}
2829
};
2930

31+
UserController.user_by_email = async function (req, res) {
32+
const { headers } = req;
33+
const { email } = req.params;
34+
35+
console.log('email: ', email);
36+
37+
if (headers['x-customrequired-header'] !== expectedHeader) {
38+
return res.sendStatus(403);
39+
}
40+
41+
try {
42+
const user = await User.find({ email });
43+
return res.status(200).send(user);
44+
} catch (err) {
45+
console.log(err);
46+
return res.sendStatus(400);
47+
}
48+
};
49+
3050
// Get list of Users with accessLevel 'admin' or 'superadmin' with GET
3151
UserController.admin_list = async function (req, res) {
3252
const { headers } = req;
@@ -104,8 +124,6 @@ UserController.user_by_id = async function (req, res) {
104124

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

297-
// Update user's managedProjects
315+
// Update user's managedProjects
298316
user.managedProjects = managedProjects;
299317
await user.save({ validateBeforeSave: false });
300318

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

330+
UserController.bulkUpdateManagedProjects = async function (req, res) {
331+
const { bulkOps } = req.body;
332+
333+
// Convert string IDs to ObjectId in bulkOps
334+
bulkOps.forEach((op) => {
335+
if (op?.updateOne?.filter._id) {
336+
op.updateOne.filter._id = ObjectId(op.updateOne.filter._id);
337+
}
338+
if (op?.updateOne?.update) {
339+
const update = op.updateOne.update;
340+
if (update?.$addToSet?.managedProjects) {
341+
update.$addToSet.managedProjects = ObjectId(update.$addToSet.managedProjects);
342+
}
343+
if (update?.$pull?.managedProjects) {
344+
update.$pull.managedProjects = ObjectId(update.$pull.managedProjects);
345+
}
346+
}
347+
});
348+
349+
try {
350+
const result = await User.bulkWrite(bulkOps);
351+
res.status(200).json(result);
352+
} catch (err) {
353+
res.status(500).json({ error: err.message });
354+
}
355+
};
356+
312357
module.exports = UserController;

backend/models/project.model.js

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,51 @@ Idea for the future: programmingLanguages, numberGithubContributions (pull these
1616
*/
1717

1818
const projectSchema = mongoose.Schema({
19-
name: { type: String, trim: true },
20-
description: { type: String, trim: true },
21-
githubIdentifier: { type: String, trim: true },
22-
projectStatus: { type: String }, // Active, Completed, or Paused
23-
location: { type: String, trim: true }, // DTLA, Westside, South LA, or Remote (hacknight)
24-
//teamMembers: { type: String }, // commented since we should be able to get this from Project Team Members table
25-
createdDate: { type: Date, default: Date.now }, // date/time project was created
26-
completedDate: { type: Date }, // only if Status = Completed, date/time completed
27-
githubUrl: { type: String, trim: true }, // link to main repo
28-
slackUrl: { type: String, trim: true }, // link to Slack channel
29-
googleDriveUrl: { type: String, trim: true },
30-
googleDriveId: { type: String },
31-
hflaWebsiteUrl: { type: String, trim: true },
32-
videoConferenceLink: { type: String },
33-
lookingDescription: { type: String }, // narrative on what the project is looking for
34-
recruitingCategories: [{ type: String }], // same as global Skills picklist
35-
partners: [{ type: String }], // any third-party partners on the project, e.g. City of LA
36-
managedByUsers: [{ type: String }] // Which users may manage this project.
19+
name: { type: String, trim: true },
20+
description: { type: String, trim: true },
21+
githubIdentifier: { type: String, trim: true },
22+
projectStatus: { type: String }, // Active, Completed, or Paused
23+
location: { type: String, trim: true }, // DTLA, Westside, South LA, or Remote (hacknight)
24+
//teamMembers: { type: String }, // commented since we should be able to get this from Project Team Members table
25+
createdDate: { type: Date, default: Date.now }, // date/time project was created
26+
completedDate: { type: Date }, // only if Status = Completed, date/time completed
27+
githubUrl: { type: String, trim: true }, // link to main repo
28+
slackUrl: { type: String, trim: true }, // link to Slack channel
29+
googleDriveUrl: { type: String, trim: true },
30+
googleDriveId: { type: String },
31+
hflaWebsiteUrl: { type: String, trim: true },
32+
videoConferenceLink: { type: String },
33+
lookingDescription: { type: String }, // narrative on what the project is looking for
34+
recruitingCategories: [{ type: String }], // same as global Skills picklist
35+
partners: [{ type: String }], // any third-party partners on the project, e.g. City of LA
36+
managedByUsers: [{ type: String }], // Which users may manage this project.
37+
onboardOffboardVisible: { type: Boolean, default: true }, // Whether onboarding/offboarding forms are visible on the project page
3738
});
3839

39-
projectSchema.methods.serialize = function() {
40-
return {
41-
id: this._id,
42-
name: this.name,
43-
description: this.description,
44-
githubIdentifier: this.githubIdentifier,
45-
// owner: this.owner,
46-
projectStatus: this.projectStatus,
47-
location: this.location,
48-
//teamMembers: this.teamMembers,
49-
createdDate: this.createdDate,
50-
completedDate: this.completedDate,
51-
githubUrl: this.githubUrl,
52-
slackUrl: this.slackUrl,
53-
googleDriveUrl: this.googleDriveUrl,
54-
googleDriveId: this.googleDriveId,
55-
hflaWebsiteUrl: this.hflaWebsiteUrl,
56-
videoConferenceLink: this.videoConferenceLink,
57-
lookingDescription: this.lookingDescription,
58-
recruitingCategories: this.recruitingCategories,
59-
partners: this.partners,
60-
managedByUsers: this.managedByUsers
61-
};
40+
projectSchema.methods.serialize = function () {
41+
return {
42+
id: this._id,
43+
name: this.name,
44+
description: this.description,
45+
githubIdentifier: this.githubIdentifier,
46+
// owner: this.owner,
47+
projectStatus: this.projectStatus,
48+
location: this.location,
49+
//teamMembers: this.teamMembers,
50+
createdDate: this.createdDate,
51+
completedDate: this.completedDate,
52+
githubUrl: this.githubUrl,
53+
slackUrl: this.slackUrl,
54+
googleDriveUrl: this.googleDriveUrl,
55+
googleDriveId: this.googleDriveId,
56+
hflaWebsiteUrl: this.hflaWebsiteUrl,
57+
videoConferenceLink: this.videoConferenceLink,
58+
lookingDescription: this.lookingDescription,
59+
recruitingCategories: this.recruitingCategories,
60+
partners: this.partners,
61+
managedByUsers: this.managedByUsers,
62+
onboardOffboardVisible: this.onboardOffboardVisible,
63+
};
6264
};
6365

6466
const Project = mongoose.model('Project', projectSchema);

backend/models/user.model.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const userSchema = mongoose.Schema({
3737
isHflaGithubMember: { type: Boolean }, // pull from API once github handle in place?
3838
githubPublic2FA: { type: Boolean }, // does the user have 2FA enabled on their github and membership set to public?
3939
availability: { type: String }, // availability to meet outside of hacknight times; string for now, more structured in future
40-
managedProjects: [{ type: String}], // Which projects managed by user.
40+
managedProjects: [{ type: String }], // Which projects managed by user.
4141
//currentProject: { type: String } // no longer need this as we can get it from Project Team Member table
4242
// password: { type: String, required: true }
4343
isActive: { type: Boolean, default: true }

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"morgan": "^1.10.0",
5757
"node-cron": "^2.0.3",
5858
"node-fetch": "^2.6.7",
59-
"nodemailer": "^6.6.1"
59+
"nodemailer": "^7.0.11"
6060
},
6161
"directories": {
6262
"test": "test"

0 commit comments

Comments
 (0)