diff --git a/backend/src/services/rating-service.ts b/backend/src/services/rating-service.ts index 298efbc..d02a281 100644 --- a/backend/src/services/rating-service.ts +++ b/backend/src/services/rating-service.ts @@ -235,17 +235,6 @@ export class RatingService implements IRatingService { throw new ForbiddenError("Only admitted users may rate projects"); } - // TODO only users in a team are allowed to vote. Prevent them from - // leaving their team to vote for themselves. However, they could leave - // their team and create a new one. So they need to be in a team that has - // been created before the rating started. I don't track the timestamp yet... - // Prevent teams from changing once rating starts is the best bet. Use the - // existing switch and prevent changes, add some text to the settings page that - // this is the case to avoid confusion. Still, people could join a second team - // before voting starts, in order to vote for their own project. I don't think - // it is possible to avoid this situation. It needs to be prohibited via the - // rules and it needs a way to check if this happened, but the ratings are - // anonymous. HOORAY. Or we allow people to rate their own project after all. if (user.team == null) { throw new ForbiddenError("You need to be part of a team to vote"); } diff --git a/backend/src/services/team-service.ts b/backend/src/services/team-service.ts index 687dea8..66b90a3 100644 --- a/backend/src/services/team-service.ts +++ b/backend/src/services/team-service.ts @@ -248,6 +248,17 @@ export class TeamService implements ITeamService { ); } + if (user.team != null) { + const oldTeam = await this._teams.findOne({ + where: { id: user.team.id }, + relations: ["users", "requests", "owner"], + }); + + if (oldTeam?.owner?.id === user.id) { + throw new Error("Make someone else owner of your other team first"); + } + } + await this._users.save({ ...user, teamRequest: team, diff --git a/backend/test/services/team-service.spec.ts b/backend/test/services/team-service.spec.ts index 114a9dc..6d59c61 100644 --- a/backend/test/services/team-service.spec.ts +++ b/backend/test/services/team-service.spec.ts @@ -88,6 +88,23 @@ describe("TeamService", () => { }); }); + describe("requestToJoinTeam", () => { + it("throws when the requester is owner of a team", async () => { + expect.assertions(1); + + const requestingUser = await userRepo.save(makeUser("req@test.com")); + const createdTeam = await teamService.createTeam( + makeTeam(), + requestingUser, + ); + await userRepo.save({ ...requestingUser, team: createdTeam }); + + await expect( + teamService.requestToJoinTeam(createdTeam.id, requestingUser), + ).rejects.toThrow("Make someone else owner of your other team first"); + }); + }); + describe("acceptUserToTeam", () => { it("throws when the requester is neither owner nor admin", async () => { expect.assertions(1); diff --git a/frontend/src/components/base/notification.tsx b/frontend/src/components/base/notification.tsx index 473cc5c..896cb14 100644 --- a/frontend/src/components/base/notification.tsx +++ b/frontend/src/components/base/notification.tsx @@ -9,6 +9,8 @@ const NotificationContainer = styled(NonGrowingFlexContainer)` right: -5rem; opacity: 0; + pointer-events: none; + padding: 0.75rem 1.5rem; text-transform: uppercase; diff --git a/frontend/src/components/pages/rating-form.tsx b/frontend/src/components/pages/rating-form.tsx index e229c5f..139b150 100644 --- a/frontend/src/components/pages/rating-form.tsx +++ b/frontend/src/components/pages/rating-form.tsx @@ -18,6 +18,8 @@ interface IRatingFormProps { project: ProjectDTO; } +const NOT_RATED = -1; + /** * Component that allows users to submit and edit ratings for projects. * Only for one criterion, use multiple of this to cover all of them. @@ -32,7 +34,9 @@ export const RatingForm = ({ const { showNotification } = useNotificationContext(); - const [ratingValue, setRatingValue] = useState(rating?.rating || 3); + const [ratingValue, setRatingValue] = useState( + rating?.rating || NOT_RATED, + ); const [isSubmitting, setIsSubmitting] = useState(false); React.useEffect(() => { @@ -42,6 +46,10 @@ export const RatingForm = ({ }, [rating]); const handleSubmit = async () => { + if (ratingValue === NOT_RATED) { + return; + } + setIsSubmitting(true); await api.createRating({ @@ -64,6 +72,13 @@ export const RatingForm = ({ value={ratingValue?.toString()} onChange={(e) => setRatingValue(parseInt(e.target.value, 10))} > + } + label="" + style={{ display: "none" }} + /> {[1, 2, 3, 4, 5].map((value) => ( { const { forcePerformRequest: sendRequestToJoin } = useApi( async (apiClient, wasTriggeredManually) => { - if (wasTriggeredManually) { + if (!wasTriggeredManually) { + return false; + } + + try { await apiClient.requestToJoinTeam(Number(params.get("id"))); showNotification("Request sent"); return true; + } catch (error) { + if (error instanceof Error) { + showNotification(error.message); + } } - return false; }, [], );