CHanged Event admin layout
This commit is contained in:
@@ -1,2 +1,6 @@
|
||||
MOODLE_TOKEN=
|
||||
MOODLE_URL=
|
||||
MOODLE_URL=
|
||||
MAIL_SERVER=
|
||||
MAIL_USER=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_PORT=
|
||||
@@ -2,6 +2,8 @@ import { getMoodleCourseCompletionStatus, getMoodleUserById } from "./moodle";
|
||||
import { CronJob } from "cron";
|
||||
import { prisma } from "@repo/db";
|
||||
import { eventCompleted } from "@repo/ui/helper";
|
||||
import { sendCourseCompletedEmail } from "modules/mail";
|
||||
import { handleParticipantFinished } from "modules/event";
|
||||
|
||||
const syncMoodleIds = async () => {
|
||||
try {
|
||||
@@ -57,7 +59,7 @@ const updateParticipantMoodleResults = async () => {
|
||||
);
|
||||
|
||||
if (quizzResult?.completionstatus?.completed === true) {
|
||||
await prisma.participant.update({
|
||||
return prisma.participant.update({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
@@ -65,7 +67,7 @@ const updateParticipantMoodleResults = async () => {
|
||||
finisherMoodleCurseCompleted: true,
|
||||
statusLog: {
|
||||
push: {
|
||||
event: "Finisher course completed",
|
||||
event: "Moodle-Kurs abgeschlossen",
|
||||
timestamp: new Date(),
|
||||
user: "system",
|
||||
},
|
||||
@@ -77,33 +79,40 @@ const updateParticipantMoodleResults = async () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const checkedFinishedParticipants = async () => {
|
||||
export const checkFinishedParticipants = async () => {
|
||||
console.log("Checking finished participants");
|
||||
const participantsPending = await prisma.participant.findMany({
|
||||
where: {
|
||||
finished: false,
|
||||
completetionWorkflowFinished: false,
|
||||
},
|
||||
include: {
|
||||
Event: true,
|
||||
User: true,
|
||||
},
|
||||
});
|
||||
|
||||
participantsPending.forEach(async (p) => {
|
||||
if (!p.User) return;
|
||||
if (!p.User.moodleId) return;
|
||||
|
||||
const completed = eventCompleted(p.Event, p);
|
||||
|
||||
if (!completed) return;
|
||||
handleParticipantFinished(p.Event, p, p.User);
|
||||
});
|
||||
};
|
||||
|
||||
CronJob.from({ cronTime: "0 * * * *", onTick: syncMoodleIds, start: true });
|
||||
CronJob.from({
|
||||
cronTime: "*/5 * * * *",
|
||||
cronTime: "*/1 * * * *",
|
||||
onTick: async () => {
|
||||
console.log("Updating participant moodle results");
|
||||
await updateParticipantMoodleResults();
|
||||
await checkFinishedParticipants();
|
||||
},
|
||||
start: true,
|
||||
});
|
||||
|
||||
updateParticipantMoodleResults();
|
||||
const debug = async () => {
|
||||
await updateParticipantMoodleResults();
|
||||
await checkFinishedParticipants();
|
||||
};
|
||||
|
||||
debug();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Event, Participant, prisma, User } from "@repo/db";
|
||||
import { sendCourseCompletedEmail } from "modules/mail";
|
||||
|
||||
export const handleParticipantFinished = async (
|
||||
event: Event,
|
||||
@@ -11,9 +12,6 @@ export const handleParticipantFinished = async (
|
||||
},
|
||||
});
|
||||
|
||||
//TODO: Send Discord Message
|
||||
//TODO: Send Email
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
@@ -22,19 +20,24 @@ export const handleParticipantFinished = async (
|
||||
badges: {
|
||||
push: event.finishedBadges,
|
||||
},
|
||||
permissions: event.finishedPermissions,
|
||||
permissions: {
|
||||
push: event.finishedPermissions,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//TODO: Send Discord Message
|
||||
await sendCourseCompletedEmail(user.email, user, event);
|
||||
|
||||
await prisma.participant.update({
|
||||
where: {
|
||||
id: participant.id,
|
||||
},
|
||||
data: {
|
||||
finished: true,
|
||||
completetionWorkflowFinished: true,
|
||||
statusLog: {
|
||||
push: {
|
||||
event: "Event finished",
|
||||
event: "Berechtigungen und Badges vergeben",
|
||||
timestamp: new Date(),
|
||||
user: "system",
|
||||
},
|
||||
|
||||
21
apps/hub-server/modules/mail-templates/CourseCompleted.tsx
Normal file
21
apps/hub-server/modules/mail-templates/CourseCompleted.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import * as React from "react";
|
||||
import { Event, User } from "@repo/db";
|
||||
|
||||
import { Html, Button, render } from "@react-email/components";
|
||||
|
||||
const Template = ({ event, user }: { user: User; event: Event }) => (
|
||||
<Html lang="en">
|
||||
<p>You completed the Course {event.name}</p>
|
||||
<p>Congratulation</p>
|
||||
</Html>
|
||||
);
|
||||
|
||||
export function renderCourseCompleted({
|
||||
user,
|
||||
event,
|
||||
}: {
|
||||
user: User;
|
||||
event: Event;
|
||||
}) {
|
||||
return render(<Template event={event} user={user} />);
|
||||
}
|
||||
59
apps/hub-server/modules/mail.ts
Normal file
59
apps/hub-server/modules/mail.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Event, User } from "@repo/db";
|
||||
import nodemailer from "nodemailer";
|
||||
import { renderCourseCompleted } from "./mail-templates/CourseCompleted";
|
||||
|
||||
let transporter: nodemailer.Transporter | null = null;
|
||||
|
||||
const initTransporter = () => {
|
||||
if (!process.env.MAIL_SERVER)
|
||||
return console.error("MAIL_SERVER is not defined");
|
||||
if (!process.env.MAIL_PORT) return console.error("MAIL_PORT is not defined");
|
||||
if (!process.env.MAIL_USER) return console.error("MAIL_USER is not defined");
|
||||
if (!process.env.MAIL_PASSWORD)
|
||||
return console.error("MAIL_PASSWORD is not defined");
|
||||
|
||||
transporter = nodemailer.createTransport({
|
||||
host: process.env.MAIL_SERVER,
|
||||
port: parseInt(process.env.MAIL_PORT),
|
||||
secure: true, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: process.env.MAIL_USER,
|
||||
pass: process.env.MAIL_PASSWORD,
|
||||
},
|
||||
});
|
||||
transporter.on("error", (err) => {
|
||||
console.error("Mail occurred:", err);
|
||||
});
|
||||
transporter.on("idle", () => {
|
||||
console.log("Mail Idle");
|
||||
});
|
||||
};
|
||||
|
||||
initTransporter();
|
||||
|
||||
export const sendCourseCompletedEmail = async (
|
||||
to: string,
|
||||
user: User,
|
||||
event: Event,
|
||||
) => {
|
||||
const emailHtml = await renderCourseCompleted({ user, event });
|
||||
|
||||
if (!transporter) {
|
||||
console.error("Transporter is not initialized");
|
||||
return;
|
||||
}
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: process.env.MAIL_USER,
|
||||
to,
|
||||
subject: `Congratulations ${user.firstname} on completing ${event.name}`,
|
||||
html: emailHtml,
|
||||
},
|
||||
(error, info) => {
|
||||
if (error) {
|
||||
console.error("Error sending email:", error);
|
||||
} else {
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -11,12 +11,16 @@
|
||||
"@repo/db": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"concurrently": "^9.1.2",
|
||||
"typescript": "latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-email/components": "^0.0.33",
|
||||
"axios": "^1.7.9",
|
||||
"cron": "^4.1.0",
|
||||
"dotenv": "^16.4.7"
|
||||
"dotenv": "^16.4.7",
|
||||
"nodemailer": "^6.10.0",
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"allowImportingTsExtensions": false,
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["**/*.ts", "./index.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
"extends": "@repo/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"allowImportingTsExtensions": false,
|
||||
"baseUrl": ".",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["**/*.ts", "./index.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user