fixed moodle logic

This commit is contained in:
PxlLoewe
2025-06-26 13:40:33 -07:00
parent 8968bff1c5
commit 22a406c2d1
13 changed files with 127 additions and 95 deletions

View File

@@ -9,6 +9,6 @@ DISCORD_BOT_TOKEN=
NEXT_PUBLIC_DISCORD_URL=
DISCORD_REDIRECT=
MOODLE_PW=var-api-user-P1
MOODLE_TOKEN=ac346f0324647b68488d13fd52a9bbe8
MOODLE_API_TOKEN=ac346f0324647b68488d13fd52a9bbe8
NEXT_PUBLIC_MOODLE_URL=http://localhost:8081
NEXT_PUBLIC_DISPATCH_URL=http://localhost:3001

View File

@@ -62,15 +62,6 @@ export const AppointmentModal = ({
timeCaption="Uhrzeit"
showTimeCaption
/>
{/* <Input
form={appointmentForm}
type="datetime-local"
label="Datum"
name="appointmentDate"
formOptions={{
valueAsDate: true,
}}
/> */}
<div>
<PaginatedTable
hide={appointmentForm.watch("id") === undefined}

View File

@@ -55,23 +55,28 @@ export const ParticipantModal = ({ participantForm, ref }: ParticipantModalProps
<Button
className="btn-sm"
onClick={async () => {
if (!participantForm.watch("id")) return;
try {
if (!participantForm.watch("id")) return;
const participant = participantForm.getValues();
await handleParticipantFinished(participant.id.toString()).catch((e) => {
const error = e as AxiosError;
const participant = participantForm.getValues();
await handleParticipantFinished(participant.id.toString()).catch((e) => {
const error = e as AxiosError;
});
toast.success("Workflow erfolgreich ausgeführt");
router.refresh();
} catch (error) {
const e = error as AxiosError;
if (
error.response?.data &&
typeof error.response.data === "object" &&
"error" in error.response.data
e.response?.data &&
typeof e.response.data === "object" &&
"error" in e.response.data
) {
toast.error(`Fehler: ${error.response.data.error}`);
toast.error(`Fehler: ${e.response.data.error}`);
} else {
toast.error("Unbekannter Fehler beim ausführen des Workflows");
}
});
toast.success("Workflow erfolgreich ausgeführt");
router.refresh();
}
}}
>
Event-abgeschlossen Workflow ausführen

View File

@@ -158,7 +158,7 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
form={form}
name="permissions"
label="Permissions"
isDisabled={user.permissions.length > (session.data?.user?.permissions?.length ?? 0)}
isDisabled={!session.data?.user.permissions.includes("ADMIN_USER_ADVANCED")}
options={Object.entries(PERMISSION).map(([key, value]) => ({
label: value,
value: key,
@@ -325,6 +325,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
export const UserPenalties = ({ user }: { user: User }) => {
const createdUser = useSession().data?.user;
const penaltyTable = useRef<PaginatedTableRef>(null);
const session = useSession();
return (
<div className="card-body">
<h2 className="card-title flex justify-between">
@@ -353,25 +354,27 @@ export const UserPenalties = ({ user }: { user: User }) => {
btnTip="Timeban hinzufügen"
showDatePicker={true}
/>
<PenaltyDropdown
Icon={<LockKeyhole size={15} />}
onClick={async ({ reason }) => {
if (!reason) return toast.error("Bitte gib einen Grund für die Strafe an.");
if (!createdUser)
return toast.error("Du musst eingeloggt sein, um eine Strafe zu erstellen.");
await addPenalty({
reason,
type: "BAN",
userId: user.id,
createdUserId: createdUser.id,
});
await editUser(user.id, { isBanned: true });
penaltyTable.current?.refresh();
toast.success("Ban wurde hinzugefügt!");
}}
btnClassName="btn btn-outline btn-error tooltip-error"
btnTip="Rechte-entzug hinzufügen"
/>
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<PenaltyDropdown
Icon={<LockKeyhole size={15} />}
onClick={async ({ reason }) => {
if (!reason) return toast.error("Bitte gib einen Grund für die Strafe an.");
if (!createdUser)
return toast.error("Du musst eingeloggt sein, um eine Strafe zu erstellen.");
await addPenalty({
reason,
type: "BAN",
userId: user.id,
createdUserId: createdUser.id,
});
await editUser(user.id, { isBanned: true });
penaltyTable.current?.refresh();
toast.success("Ban wurde hinzugefügt!");
}}
btnClassName="btn btn-outline btn-error tooltip-error"
btnTip="Nutzerkonto sperren"
/>
)}
</div>
</h2>
<PaginatedTable
@@ -484,7 +487,7 @@ export const AdminForm = ({
</Button>
</div>
)}
{user.isBanned && (
{user.isBanned && session?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<Button
onClick={async () => {
await editUser(user.id, { isBanned: false });

View File

@@ -1,5 +1,5 @@
"use client";
import { useEffect } from "react";
import { use, useEffect } from "react";
import { CheckCircledIcon, CalendarIcon, EnterIcon } from "@radix-ui/react-icons";
import { Event, EventAppointment, Participant, User } from "@repo/db";
import { cn } from "../../../../helper/cn";
@@ -87,6 +87,10 @@ const ModalBtn = ({
? (selectedDate as any)?.Participants?.length + 1
: ownIndexInParticipantList + 1;
const missingRequirements =
event.requiredBadges?.length > 0 &&
!event.requiredBadges.some((badge) => user.badges.includes(badge));
return (
<>
<button
@@ -96,14 +100,20 @@ const ModalBtn = ({
eventCompleted(event, participant) && "btn-success",
)}
onClick={openModal}
disabled={eventCompleted(event, participant)}
disabled={eventCompleted(event, participant) || missingRequirements}
>
{participant && !eventCompleted(event, participant) && (
{missingRequirements && (
<>
<TriangleAlert className="h-6 w-6 shrink-0 stroke-current" fill="none" />
fehlende Anforderungen
</>
)}
{participant && !eventCompleted(event, participant) && !missingRequirements && (
<>
<EyeIcon /> Anzeigen
</>
)}
{!participant && (
{!participant && !missingRequirements && (
<>
<EnterIcon /> Anmelden
</>
@@ -287,16 +297,20 @@ const MoodleCourseIndicator = ({
<button
className="btn btn-xs btn-info ml-2"
onClick={async () => {
await upsertParticipant({
eventId: event.id,
userId: user.id,
finisherMoodleCurseCompleted: false,
});
try {
await upsertParticipant({
eventId: event.id,
userId: user.id,
finisherMoodleCurseCompleted: false,
});
if (user.moodleId) {
await inscribeToMoodleCourse(moodleCourseId, user.moodleId);
if (user.moodleId) {
await inscribeToMoodleCourse(moodleCourseId, user.moodleId);
}
window.open(courseUrl, "_blank");
} catch (error) {
console.log("Fehler beim öffnen des Moodle-Kurses", error);
}
window.open(courseUrl, "_blank");
}}
>
Zum Moodle Kurs

View File

@@ -11,21 +11,12 @@ export default async function Page() {
const user = await prisma.user.findFirst({
where: {
id: session.user.id,
Penaltys: {
some: {
until: {
gte: new Date(),
},
suspended: false,
},
},
},
include: {
discordAccounts: true,
Penaltys: true,
},
});
console.log("User", session, user);
const userPenaltys = await prisma.penalty.findMany({
where: {
userId: session.user.id,

View File

@@ -33,7 +33,7 @@ export const VerticalNav = async () => {
<li>
<Link href="/logbook">
<ReaderIcon />
Logbook
Logbuch
</Link>
</li>
<li>

View File

@@ -5,6 +5,7 @@ import { DiscordAccount, prisma, User } from "@repo/db";
import bcrypt from "bcryptjs";
import oldUser from "./var.User.json";
import { OldUser } from "../../../../types/oldUser";
import { sendVerificationLink } from "(app)/admin/user/action";
export const options: AuthOptions = {
providers: [
@@ -69,6 +70,7 @@ export const options: AuthOptions = {
},
});
}
await sendVerificationLink(newUser.id);
return newUser;
}
}

View File

@@ -25,6 +25,7 @@ export const GET = async (req: NextRequest) => {
if (!user) return NextResponse.json({ error: "User not found" }, { status: 404 });
setTimeout(async () => {
const moodleUser = await getMoodleUserById(user.id);
await prisma.user.update({
where: {
id: user.id,

View File

@@ -1,37 +1,44 @@
import axios from "axios";
export const enrollUserInCourse = async (
courseid: number | string,
userid: number | string,
) => {
const { data: enrollmentResponse } = await axios.get(
`${process.env.NEXT_PUBLIC_MOODLE_URL}/webservice/rest/server.php`,
{
params: {
wstoken: process.env.MOODLE_TOKEN,
wsfunction: "enrol_manual_enrol_users",
moodlewsrestformat: "json",
enrolments: [
{
roleid: 5,
userid: typeof userid === "string" ? parseInt(userid) : userid,
courseid:
typeof courseid === "string" ? parseInt(courseid) : courseid,
},
],
export const enrollUserInCourse = async (courseid: number | string, userid: number | string) => {
try {
const { data: enrollmentResponse } = await axios.get(
`${process.env.NEXT_PUBLIC_MOODLE_URL}/webservice/rest/server.php`,
{
auth: {
username: "moodleuser",
password: "Xo1SXaLYBa7Yb6WW",
},
params: {
wstoken: process.env.MOODLE_API_TOKEN,
wsfunction: "enrol_manual_enrol_users",
moodlewsrestformat: "json",
enrolments: [
{
roleid: 5,
userid: typeof userid === "string" ? parseInt(userid) : userid,
courseid: typeof courseid === "string" ? parseInt(courseid) : courseid,
},
],
},
},
},
);
if (enrollmentResponse !== null) console.error(enrollmentResponse);
return enrollmentResponse;
);
return enrollmentResponse;
} catch (error) {
return new Error("Failed to enroll user in course");
}
};
export const getMoodleUserById = async (id: string) => {
const { data: user } = await axios.get(
`${process.env.NEXT_PUBLIC_MOODLE_URL}/webservice/rest/server.php`,
{
auth: {
username: "moodleuser",
password: "Xo1SXaLYBa7Yb6WW",
},
params: {
wstoken: process.env.MOODLE_TOKEN,
wstoken: process.env.MOODLE_API_TOKEN,
wsfunction: "core_user_get_users_by_field",
moodlewsrestformat: "json",
field: "idnumber",
@@ -42,6 +49,7 @@ export const getMoodleUserById = async (id: string) => {
},
},
);
console.log("Moodle User", user);
const u = user[0];
return (
(u as {