Skip to content

Commit 095936d

Browse files
authored
Merge pull request #146 from MathisBurger/feature/switch-to-tutor-account
Switch to tutor
2 parents c630c7b + 360bd2d commit 095936d

File tree

8 files changed

+131
-28
lines changed

8 files changed

+131
-28
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package controllers
2+
3+
import (
4+
"github.com/gofiber/fiber/v2"
5+
"usernator/internal/models"
6+
"usernator/internal/shared"
7+
"usernator/internal/util"
8+
)
9+
10+
// / Endpoint to switch to tutor
11+
func SwitchToTutor(ctx *fiber.Ctx) error {
12+
currentUser, ok := ctx.Locals("currentUser").(*models.User)
13+
if !ok {
14+
return fiber.NewError(fiber.StatusUnauthorized, "You need to be logged in")
15+
}
16+
if !util.ArrayContains(currentUser.Roles, "ROLE_STUDENT") {
17+
return fiber.NewError(fiber.StatusForbidden, "You need to be a student in order to switch to tutor account")
18+
}
19+
20+
shared.Database.Exec("UPDATE users SET roles = '{ROLE_TUTOR}' WHERE id = ?", currentUser.ID)
21+
ctx.ClearCookie("session")
22+
return ctx.SendStatus(fiber.StatusOK)
23+
}

usernator/internal/server/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func CreateServer(configPath string) *fiber.App {
3535
app.Post("/register", controllers.RegisterUser)
3636
app.Post("/login", controllers.LoginUser)
3737
app.Post("/create_tutor", controllers.CreateTutor)
38+
app.Post("/switch_tutor", controllers.SwitchToTutor)
3839

3940
// These endpoints are currently disabled and therefore not accessed
4041
//app.Post("/reset_password", controllers.ResetPassword)

usernator/internal/util/array.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package util
2+
3+
// / Checks if an array contains a specific value
4+
func ArrayContains(a []string, x string) bool {
5+
for _, n := range a {
6+
if x == n {
7+
return true
8+
}
9+
}
10+
return false
11+
}

web/app/settings/page.tsx

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
"use client";
22

33
import {
4+
Button,
45
Combobox,
56
Container,
67
Input,
78
InputBase,
89
MantineColorScheme,
10+
Stack,
911
Title,
1012
useCombobox,
1113
useMantineColorScheme,
1214
} from "@mantine/core";
13-
import { useTranslation } from "react-i18next";
15+
import {useTranslation} from "react-i18next";
16+
import useCurrentUser from "@/hooks/useCurrentUser";
17+
import {isGranted} from "@/service/auth";
18+
import {UserRoles} from "@/service/types/usernator";
19+
import {useState} from "react";
20+
import SwitchToTutorModal from "@/components/SwitchToTutorModal";
1421

1522
const schemes = ["light", "dark", "auto"];
1623

@@ -22,6 +29,8 @@ const SettingsPage = () => {
2229
onDropdownClose: () => combobox.resetSelectedOption(),
2330
});
2431
const { t } = useTranslation("common");
32+
const {user} = useCurrentUser();
33+
const [switchModalOpen, setSwitchModalOpen] = useState<boolean>(false);
2534

2635
const options = schemes.map((item) => (
2736
<Combobox.Option value={item} key={item}>
@@ -32,30 +41,38 @@ const SettingsPage = () => {
3241
return (
3342
<Container fluid>
3443
<Title>{t("settings.settings")}</Title>
35-
<Combobox
36-
store={combobox}
37-
withinPortal={false}
38-
onOptionSubmit={(val) => {
39-
setColorScheme(val as MantineColorScheme);
40-
}}
41-
>
42-
<Combobox.Target>
43-
<InputBase
44-
component="button"
45-
type="button"
46-
pointer
47-
rightSection={<Combobox.Chevron />}
48-
onClick={() => combobox.toggleDropdown()}
49-
rightSectionPointerEvents="none"
50-
>
51-
{colorScheme || <Input.Placeholder>Pick value</Input.Placeholder>}
52-
</InputBase>
53-
</Combobox.Target>
44+
<Stack gap={25} mt={10}>
45+
<Combobox
46+
store={combobox}
47+
withinPortal={false}
48+
onOptionSubmit={(val) => {
49+
setColorScheme(val as MantineColorScheme);
50+
}}
51+
>
52+
<Combobox.Target>
53+
<InputBase
54+
component="button"
55+
type="button"
56+
pointer
57+
rightSection={<Combobox.Chevron />}
58+
onClick={() => combobox.toggleDropdown()}
59+
rightSectionPointerEvents="none"
60+
>
61+
{colorScheme || <Input.Placeholder>Pick value</Input.Placeholder>}
62+
</InputBase>
63+
</Combobox.Target>
5464

55-
<Combobox.Dropdown>
56-
<Combobox.Options>{options}</Combobox.Options>
57-
</Combobox.Dropdown>
58-
</Combobox>
65+
<Combobox.Dropdown>
66+
<Combobox.Options>{options}</Combobox.Options>
67+
</Combobox.Dropdown>
68+
</Combobox>
69+
{isGranted(user, [UserRoles.Student]) && (
70+
<Button variant="gradient" w="25%" onClick={() => setSwitchModalOpen(true)}>{t('common:titles.switch-to-tutor')}</Button>
71+
)}
72+
</Stack>
73+
{switchModalOpen && (
74+
<SwitchToTutorModal onClose={() => setSwitchModalOpen(false)} />
75+
)}
5976
</Container>
6077
);
6178
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {Button, Group, Modal, Text} from "@mantine/core";
2+
import {useTranslation} from "react-i18next";
3+
import useApiServiceClient from "@/hooks/useApiServiceClient";
4+
import {showNotification} from "@mantine/notifications";
5+
6+
interface SwitchToTutorModalProps {
7+
onClose: () => void;
8+
}
9+
10+
const SwitchToTutorModal = ({onClose}: SwitchToTutorModalProps) => {
11+
12+
const {t} = useTranslation('common');
13+
const api = useApiServiceClient();
14+
15+
const switchAccount = async () => {
16+
try {
17+
await api.switchToTutorAccount();
18+
document.cookie = 'session=""';
19+
window.location.reload();
20+
} catch (e: any) {
21+
showNotification({
22+
title: t('common:messages.error'),
23+
message: e?.message ?? "" ,
24+
})
25+
}
26+
}
27+
28+
return (
29+
<Modal opened onClose={onClose}>
30+
<Text>
31+
{t('common:messages.switch-to-tutor-text')}
32+
</Text>
33+
<Group mt={10}>
34+
<Button onClick={switchAccount}>{t("actions.save")}</Button>
35+
<Button onClick={onClose} color="gray">
36+
{t("actions.cancel")}
37+
</Button>
38+
</Group>
39+
</Modal>
40+
);
41+
}
42+
43+
export default SwitchToTutorModal;

web/public/locales/de/common.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"log-out": "Abmelden",
1313
"create-group": "Gruppe erstellen",
1414
"update-group": "Gruppe aktualisieren",
15-
"notifications": "Benachrichtigungen"
15+
"notifications": "Benachrichtigungen",
16+
"switch-to-tutor": "Zu einem Tutoren-Konto wechseln"
1617
},
1718
"cols": {
1819
"id": "ID",
@@ -57,7 +58,8 @@
5758
"created-report": "Bericht erstellt",
5859
"no-files-selected": "Keine Dateien ausgewählt",
5960
"copied-code": "Code kopiert!",
60-
"rejected-files": "Abgelehnte Dateien"
61+
"rejected-files": "Abgelehnte Dateien",
62+
"switch-to-tutor-text": "Bist du sicher, dass du zu einem Tutor-Konto wechseln möchtest? Das bedeutet, dass du keine Aufgaben mehr bearbeiten kannst. Dsfür benötigst du einen weiteren Account. Also Tutor kannst du nur Aufgaben erstellen und verwalten innerhalb deiner eigenen Gruppen. Sei dir also deiner Entscheidung bewusst"
6163
},
6264
"errors": {
6365
"title-empty": "Der Titel darf nicht leer sein",

web/public/locales/en/common.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"log-out": "Log out",
1313
"create-group": "Create group",
1414
"update-group": "Update group",
15-
"notifications": "Notifications"
15+
"notifications": "Notifications",
16+
"switch-to-tutor": "Switch to tutor account"
1617
},
1718
"cols": {
1819
"id": "ID",
@@ -57,7 +58,8 @@
5758
"created-report": "Created report",
5859
"no-files-selected": "No files selected",
5960
"copied-code": "Copied code!",
60-
"rejected-files": "Rejected files"
61+
"rejected-files": "Rejected files",
62+
"switch-to-tutor-text": "Are you sure you want to switch to a tutor account? This means you will no longer be able to work on tasks. For that, you would need a separate account. As a tutor, you can only create and manage tasks within your own groups. Please be sure of your decision."
6163
},
6264
"errors": {
6365
"title-empty": "The title should not be empty",

web/service/ApiService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ class ApiService {
313313
await this.post<any>(`/tasky/groups/${groupId}/enlist/${userId}`, {});
314314
}
315315

316+
public async switchToTutorAccount(): Promise<void> {
317+
await this.post<any>("/usernator/switch_tutor", {});
318+
}
319+
316320
public async createOrUpdateCodeTests(
317321
groupId: number,
318322
assignmentId: number,

0 commit comments

Comments
 (0)