Skip to content

Commit

Permalink
Merge pull request #2731 from Northeastern-Electric-Racing/#2708-edit…
Browse files Browse the repository at this point in the history
…-team-type-description-endpoint

#2708 Hook up edit and create team type modals and upload team type image
  • Loading branch information
walker-sean authored Oct 4, 2024
2 parents 3614bea + aa7c954 commit 432fceb
Show file tree
Hide file tree
Showing 26 changed files with 721 additions and 214 deletions.
54 changes: 45 additions & 9 deletions src/backend/src/controllers/teams.controllers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NextFunction, Request, Response } from 'express';
import TeamsService from '../services/teams.services';
import { HttpException } from '../utils/errors.utils';

export default class TeamsController {
static async getAllTeams(req: Request, res: Response, next: NextFunction) {
Expand Down Expand Up @@ -114,12 +115,14 @@ export default class TeamsController {
}
}

static async createTeamType(req: Request, res: Response, next: NextFunction) {
static async setTeamType(req: Request, res: Response, next: NextFunction) {
try {
const { name, iconName } = req.body;
const { teamTypeId } = req.body;
const { teamId } = req.params;

const createdTeamType = await TeamsService.createTeamType(req.currentUser, name, iconName, req.organization);
return res.status(200).json(createdTeamType);
const updatedTeam = await TeamsService.setTeamType(req.currentUser, teamId, teamTypeId, req.organization);

return res.status(200).json(updatedTeam);
} catch (error: unknown) {
return next(error);
}
Expand All @@ -146,14 +149,47 @@ export default class TeamsController {
}
}

static async setTeamType(req: Request, res: Response, next: NextFunction) {
static async createTeamType(req: Request, res: Response, next: NextFunction) {
try {
const { teamTypeId } = req.body;
const { teamId } = req.params;
const { name, iconName, description } = req.body;

const updatedTeam = await TeamsService.setTeamType(req.currentUser, teamId, teamTypeId, req.organization);
const createdTeamType = await TeamsService.createTeamType(
req.currentUser,
name,
iconName,
description,
req.organization
);
res.status(200).json(createdTeamType);
} catch (error: unknown) {
next(error);
}
}

return res.status(200).json(updatedTeam);
static async editTeamType(req: Request, res: Response, next: NextFunction) {
try {
const { name, iconName, description } = req.body;

const teamType = await TeamsService.editTeamType(
req.currentUser,
req.params.teamTypeId,
name,
iconName,
description,
req.organization
);
res.status(200).json(teamType);
} catch (error: unknown) {
next(error);
}
}

static async setTeamTypeImage(req: Request, res: Response, next: NextFunction) {
try {
const { file } = req;
if (!file) throw new HttpException(400, 'Invalid or undefined image data');
const teamType = await TeamsService.setTeamTypeImage(req.currentUser, req.params.teamTypeId, file, req.organization);
res.status(200).json(teamType);
} catch (error: unknown) {
return next(error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE "Team_Type"
ADD COLUMN "description" TEXT NOT NULL DEFAULT 'Default description',
ADD COLUMN "imageFileId" TEXT;

ALTER TABLE "Team_Type"
ALTER COLUMN "description" DROP DEFAULT;
2 changes: 2 additions & 0 deletions src/backend/src/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ model Team_Type {
iconName String
designReviews Design_Review[]
team Team[]
description String
imageFileId String?
organizationId String
organization Organization @relation(fields: [organizationId], references: [organizationId])
calendarId String?
Expand Down
6 changes: 3 additions & 3 deletions src/backend/src/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ const performSeed: () => Promise<void> = async () => {
* TEAMS
*/
/** Creating Team Types */
const teamType1 = await TeamsService.createTeamType(batman, 'Mechanical', 'YouTubeIcon', ner);
const teamType2 = await TeamsService.createTeamType(thomasEmrax, 'Software', 'InstagramIcon', ner);
const teamType3 = await TeamsService.createTeamType(cyborg, 'Electrical', 'SettingsIcon', ner);
const teamType1 = await TeamsService.createTeamType(batman, 'Mechanical', 'YouTubeIcon', '', ner);
const teamType2 = await TeamsService.createTeamType(thomasEmrax, 'Software', 'InstagramIcon', '', ner);
const teamType3 = await TeamsService.createTeamType(cyborg, 'Electrical', 'SettingsIcon', '', ner);

/** Creating Teams */
const justiceLeague: Team = await prisma.team.create(dbSeedAllTeams.justiceLeague(batman.userId, organizationId));
Expand Down
23 changes: 22 additions & 1 deletion src/backend/src/routes/teams.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import express from 'express';
import TeamsController from '../controllers/teams.controllers';
import { body } from 'express-validator';
import { nonEmptyString, validateInputs } from '../utils/validation.utils';
import multer, { memoryStorage } from 'multer';

const teamsRouter = express.Router();
const upload = multer({ limits: { fileSize: 30000000 }, storage: memoryStorage() });

teamsRouter.get('/', TeamsController.getAllTeams);
teamsRouter.get('/:teamId', TeamsController.getSingleTeam);
Expand All @@ -27,6 +29,7 @@ teamsRouter.post(
validateInputs,
TeamsController.editDescription
);

teamsRouter.post('/:teamId/set-head', nonEmptyString(body('userId')), validateInputs, TeamsController.setTeamHead);
teamsRouter.post('/:teamId/delete', TeamsController.deleteTeam);
teamsRouter.post(
Expand All @@ -47,13 +50,31 @@ teamsRouter.get('/teamType/all', TeamsController.getAllTeamTypes);

teamsRouter.get('/teamType/:teamTypeId/single', TeamsController.getSingleTeamType);

teamsRouter.post('/:teamId/set-team-type', nonEmptyString(body('teamTypeId')), validateInputs, TeamsController.setTeamType);

teamsRouter.post(
'/teamType/create',
nonEmptyString(body('name')),
nonEmptyString(body('iconName')),
nonEmptyString(body('description')),
validateInputs,
TeamsController.createTeamType
);

teamsRouter.post('/:teamId/set-team-type', nonEmptyString(body('teamTypeId')), validateInputs, TeamsController.setTeamType);
teamsRouter.post(
'/teamType/:teamTypeId/edit',
nonEmptyString(body('name')),
nonEmptyString(body('iconName')),
nonEmptyString(body('description')),
validateInputs,
TeamsController.editTeamType
);

teamsRouter.post(
'/teamType/:teamTypeId/set-image',
upload.single('image'),
validateInputs,
TeamsController.setTeamTypeImage
);

export default teamsRouter;
79 changes: 79 additions & 0 deletions src/backend/src/services/teams.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getPrismaQueryUserIds, getUsers, userHasPermission } from '../utils/use
import { isUnderWordCount } from 'shared';
import { removeUsersFromList } from '../utils/teams.utils';
import { getTeamQueryArgs } from '../prisma-query-args/teams.query-args';
import { uploadFile } from '../utils/google-integration.utils';
import { createCalendar } from '../utils/google-integration.utils';

export default class TeamsService {
Expand Down Expand Up @@ -387,6 +388,7 @@ export default class TeamsService {
submitter: User,
name: string,
iconName: string,
description: string,
organization: Organization
): Promise<TeamType> {
if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) {
Expand All @@ -406,6 +408,7 @@ export default class TeamsService {
data: {
name,
iconName,
description,
organizationId: organization.organizationId,
calendarId: teamTypeCalendarId
}
Expand Down Expand Up @@ -442,6 +445,50 @@ export default class TeamsService {
return teamTypes;
}

/**
* Changes the description of the given teamType to be the new description
* @param user The user who is editing the description
* @param teamTypeId The id for the teamType that is being edited
* @param name the new name for the team
* @param iconName the new icon name for the team
* @param description the new description for the team
* @param imageFileId the new image for the team
* @param organizationId The organization the user is currently in
* @returns The team with the new description
*/
static async editTeamType(
user: User,
teamTypeId: string,
name: string,
iconName: string,
description: string,
organization: Organization
): Promise<TeamType> {
if (!isUnderWordCount(description, 300)) throw new HttpException(400, 'Description must be less than 300 words');

if (!(await userHasPermission(user.userId, organization.organizationId, isAdmin)))
throw new AccessDeniedException('you must be an admin to edit the team types description');

const currentTeamType = await prisma.team_Type.findUnique({
where: { teamTypeId }
});

if (!currentTeamType) {
throw new NotFoundException('Team Type', teamTypeId);
}

const updatedTeamType = await prisma.team_Type.update({
where: { teamTypeId },
data: {
name,
iconName,
description
}
});

return updatedTeamType;
}

/**
* Sets the teamType for a team
* @param submitter the user who is setting the team type
Expand Down Expand Up @@ -482,4 +529,36 @@ export default class TeamsService {

return teamTransformer(updatedTeam);
}

static async setTeamTypeImage(
submitter: User,
teamTypeId: string,
image: Express.Multer.File,
organization: Organization
) {
if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) {
throw new AccessDeniedAdminOnlyException('set a team types image');
}

const teamType = await prisma.team_Type.findUnique({
where: {
teamTypeId
}
});

if (!teamType) throw new NotFoundException('Team Type', teamTypeId);

const imageData = await uploadFile(image);

const updatedTeamType = await prisma.team_Type.update({
where: {
teamTypeId
},
data: {
imageFileId: imageData.id
}
});

return updatedTeamType;
}
}
11 changes: 4 additions & 7 deletions src/backend/src/utils/google-integration.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,12 @@ export const uploadFile = async (fileObject: Express.Multer.File) => {
const gError = error as GoogleDriveError;
throw new HttpException(
gError.code,
`Failed to Upload Receipt(s): ${gError.message}, ${gError.errors.reduce(
(acc: string, curr: GoogleDriveErrorListError) => {
return acc + ' ' + curr.message + ' ' + curr.reason;
},
''
)}`
`Failed to Upload : ${gError.message}, ${gError.errors.reduce((acc: string, curr: GoogleDriveErrorListError) => {
return acc + ' ' + curr.message + ' ' + curr.reason;
}, '')}`
);
} else if (error instanceof Error) {
throw new HttpException(500, `Failed to Upload Receipt(s): ${error.message}`);
throw new HttpException(500, `Failed to Upload : ${error.message}`);
}
console.log('error' + error);
throw error;
Expand Down
4 changes: 3 additions & 1 deletion src/backend/tests/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,9 @@ export const createTestDesignReview = async () => {
if (!head) throw new Error('Failed to find user');
if (!lead) throw new Error('Failed to find user');
await createTestProject(head, organization.organizationId);
const teamType = await TeamsService.createTeamType(head, 'Team1', 'Software', organization);

const teamType = await TeamsService.createTeamType(head, 'Team1', 'Software', 'Software team', organization);

const { designReviewId } = await DesignReviewsService.createDesignReview(
lead,
'03/25/2027',
Expand Down
Loading

0 comments on commit 432fceb

Please sign in to comment.