Skip to content

Commit 49c2d9e

Browse files
authored
No default rating selected. Select new owner before requesting (#128)
1 parent ab7d86d commit 49c2d9e

6 files changed

Lines changed: 55 additions & 14 deletions

File tree

backend/src/services/rating-service.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,6 @@ export class RatingService implements IRatingService {
235235
throw new ForbiddenError("Only admitted users may rate projects");
236236
}
237237

238-
// TODO only users in a team are allowed to vote. Prevent them from
239-
// leaving their team to vote for themselves. However, they could leave
240-
// their team and create a new one. So they need to be in a team that has
241-
// been created before the rating started. I don't track the timestamp yet...
242-
// Prevent teams from changing once rating starts is the best bet. Use the
243-
// existing switch and prevent changes, add some text to the settings page that
244-
// this is the case to avoid confusion. Still, people could join a second team
245-
// before voting starts, in order to vote for their own project. I don't think
246-
// it is possible to avoid this situation. It needs to be prohibited via the
247-
// rules and it needs a way to check if this happened, but the ratings are
248-
// anonymous. HOORAY. Or we allow people to rate their own project after all.
249238
if (user.team == null) {
250239
throw new ForbiddenError("You need to be part of a team to vote");
251240
}

backend/src/services/team-service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,17 @@ export class TeamService implements ITeamService {
248248
);
249249
}
250250

251+
if (user.team != null) {
252+
const oldTeam = await this._teams.findOne({
253+
where: { id: user.team.id },
254+
relations: ["users", "requests", "owner"],
255+
});
256+
257+
if (oldTeam?.owner?.id === user.id) {
258+
throw new Error("Make someone else owner of your other team first");
259+
}
260+
}
261+
251262
await this._users.save({
252263
...user,
253264
teamRequest: team,

backend/test/services/team-service.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@ describe("TeamService", () => {
8888
});
8989
});
9090

91+
describe("requestToJoinTeam", () => {
92+
it("throws when the requester is owner of a team", async () => {
93+
expect.assertions(1);
94+
95+
const requestingUser = await userRepo.save(makeUser("req@test.com"));
96+
const createdTeam = await teamService.createTeam(
97+
makeTeam(),
98+
requestingUser,
99+
);
100+
await userRepo.save({ ...requestingUser, team: createdTeam });
101+
102+
await expect(
103+
teamService.requestToJoinTeam(createdTeam.id, requestingUser),
104+
).rejects.toThrow("Make someone else owner of your other team first");
105+
});
106+
});
107+
91108
describe("acceptUserToTeam", () => {
92109
it("throws when the requester is neither owner nor admin", async () => {
93110
expect.assertions(1);

frontend/src/components/base/notification.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const NotificationContainer = styled(NonGrowingFlexContainer)`
99
right: -5rem;
1010
opacity: 0;
1111
12+
pointer-events: none;
13+
1214
padding: 0.75rem 1.5rem;
1315
1416
text-transform: uppercase;

frontend/src/components/pages/rating-form.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ interface IRatingFormProps {
1818
project: ProjectDTO;
1919
}
2020

21+
const NOT_RATED = -1;
22+
2123
/**
2224
* Component that allows users to submit and edit ratings for projects.
2325
* Only for one criterion, use multiple of this to cover all of them.
@@ -32,7 +34,9 @@ export const RatingForm = ({
3234

3335
const { showNotification } = useNotificationContext();
3436

35-
const [ratingValue, setRatingValue] = useState<number>(rating?.rating || 3);
37+
const [ratingValue, setRatingValue] = useState<number>(
38+
rating?.rating || NOT_RATED,
39+
);
3640
const [isSubmitting, setIsSubmitting] = useState(false);
3741

3842
React.useEffect(() => {
@@ -42,6 +46,10 @@ export const RatingForm = ({
4246
}, [rating]);
4347

4448
const handleSubmit = async () => {
49+
if (ratingValue === NOT_RATED) {
50+
return;
51+
}
52+
4553
setIsSubmitting(true);
4654

4755
await api.createRating({
@@ -64,6 +72,13 @@ export const RatingForm = ({
6472
value={ratingValue?.toString()}
6573
onChange={(e) => setRatingValue(parseInt(e.target.value, 10))}
6674
>
75+
<FormControlLabel
76+
key="hidden"
77+
value={NOT_RATED.toString()}
78+
control={<Radio disabled={isSubmitting} />}
79+
label=""
80+
style={{ display: "none" }}
81+
/>
6782
{[1, 2, 3, 4, 5].map((value) => (
6883
<FormControlLabel
6984
key={value.toString()}

frontend/src/components/pages/read-only-team.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,19 @@ export const ReadOnlyTeam = ({ team }: { team: TeamResponseDTO }) => {
2525

2626
const { forcePerformRequest: sendRequestToJoin } = useApi(
2727
async (apiClient, wasTriggeredManually) => {
28-
if (wasTriggeredManually) {
28+
if (!wasTriggeredManually) {
29+
return false;
30+
}
31+
32+
try {
2933
await apiClient.requestToJoinTeam(Number(params.get("id")));
3034
showNotification("Request sent");
3135
return true;
36+
} catch (error) {
37+
if (error instanceof Error) {
38+
showNotification(error.message);
39+
}
3240
}
33-
return false;
3441
},
3542
[],
3643
);

0 commit comments

Comments
 (0)