removed event-chronjobs, used Events in hub-app insteand, added admin Btn to set Discord-User and run Event-completed-workflow. Fixed Bug of wrong participants-count in Event-Modal

This commit is contained in:
PxlLoewe
2025-06-05 23:02:34 -07:00
parent 91d811e289
commit 587884dfd9
21 changed files with 341 additions and 232 deletions

View File

@@ -0,0 +1,57 @@
import { DISCORD_ROLES, Event, getPublicUser, Participant, prisma } from "@repo/db";
import { Router } from "express";
import { changeMemberRoles, getMember } from "routes/member";
const router: Router = Router();
export const eventCompleted = (event: Event, participant?: Participant) => {
if (!participant) return false;
if (event.finisherMoodleCourseId && !participant.finisherMoodleCurseCompleted) return false;
if (event.hasPresenceEvents && !participant.attended) return false;
return true;
};
router.post("/set-standard-name", async (req, res) => {
const { memberId, userId } = req.body;
const user = await prisma.user.findUnique({
where: {
id: userId,
},
});
if (!user) {
res.status(404).json({ error: "User not found" });
return;
}
const participant = await prisma.participant.findMany({
where: {
userId: user.id,
},
include: {
Event: true,
},
});
let eventRoles: string[] = [];
participant.forEach(async (p) => {
if (!p.Event.discordRoleId) return;
if (eventCompleted(p.Event, p)) {
await changeMemberRoles(memberId, [p.Event.discordRoleId], "remove");
} else {
await changeMemberRoles(memberId, [p.Event.discordRoleId], "add");
}
});
const publicUser = getPublicUser(user);
const member = await getMember(memberId);
await member.setNickname(`${publicUser.fullName} (${user.publicId})`);
const isPilot = user.permissions.includes("PILOT");
const isDispatcher = user.permissions.includes("DISPO");
await changeMemberRoles(memberId, [DISCORD_ROLES.PILOT], isPilot ? "add" : "remove");
await changeMemberRoles(memberId, [DISCORD_ROLES.DISPATCHER], isDispatcher ? "add" : "remove");
});
export default router;

View File

@@ -8,7 +8,7 @@ if (!GUILD_ID) {
const router: Router = Router();
const getMember = async (memberId: string) => {
export const getMember = async (memberId: string) => {
const guild = client.guilds.cache.get(GUILD_ID);
if (!guild) throw new Error("Guild not found");
try {
@@ -36,13 +36,11 @@ router.post("/rename", async (req: Request, res: Response) => {
}
});
const handleRoleChange = (action: "add" | "remove") => async (req: Request, res: Response) => {
const { roleIds, memberId } = req.body;
if (!Array.isArray(roleIds) || !memberId) {
res.status(400).json({ error: "Invalid or missing roleIds or memberId" });
return;
}
try {
export const changeMemberRoles = async (
memberId: string,
roleIds: string[],
action: "add" | "remove",
) => {
const member = await getMember(memberId);
const currentRoleIds = member.roles.cache.map((role) => role.id);
@@ -51,18 +49,23 @@ const handleRoleChange = (action: "add" | "remove") => async (req: Request, res:
? roleIds.filter((id: string) => !currentRoleIds.includes(id))
: roleIds.filter((id: string) => currentRoleIds.includes(id));
console.log(
`Attempting to ${action} roles: ${filteredRoleIds.join(", ")} to member ${member.nickname || member.user.username}`,
);
// Option to skip if no roles to add/remove
if (filteredRoleIds.length === 0) {
console.log(`No roles to ${action}`);
res.status(200).json({ message: `No roles to ${action}` });
return;
return { message: `No roles to ${action}` };
}
await member.roles[action](roleIds);
res.status(200).json({ message: `Roles ${action}ed successfully` });
await member.roles[action](filteredRoleIds);
return { message: `Roles ${action}ed successfully` };
};
const handleRoleChange = (action: "add" | "remove") => async (req: Request, res: Response) => {
const { roleIds, memberId } = req.body;
if (!Array.isArray(roleIds) || !memberId) {
res.status(400).json({ error: "Invalid or missing roleIds or memberId" });
return;
}
try {
const result = await changeMemberRoles(memberId, roleIds, action);
res.status(200).json(result);
} catch (error) {
console.error(`Error ${action}ing roles:`, error);
res.status(500).json({ error: `Failed to ${action} roles` });

View File

@@ -1,8 +1,10 @@
import { Router } from "express";
import memberRouter from "./member";
import helperRouter from "./helper";
const router: Router = Router();
router.use("/member", memberRouter);
router.use("/helper", helperRouter);
export default router;

View File

@@ -1,11 +1,7 @@
import { getMoodleCourseCompletionStatus, getMoodleUserById } from "./moodle";
import { CronJob } from "cron";
import { DISCORD_ROLES, ParticipantLog, prisma } from "@repo/db";
import { sendCourseCompletedEmail } from "modules/mail";
import { prisma } from "@repo/db";
import { handleParticipantFinished } from "modules/event";
import { eventCompleted } from "helper/events";
import { addRolesToMember, removeRolesFromMember } from "modules/discord";
import { JsonValueType } from "@repo/db/zod";
const syncMoodleIds = async () => {
try {
@@ -61,6 +57,8 @@ const updateParticipantMoodleResults = async () => {
);
if (quizzResult?.completionstatus?.completed === true) {
await handleParticipantFinished(p.Event, p, p.User);
return prisma.participant.update({
where: {
id: p.id,
@@ -81,127 +79,11 @@ const updateParticipantMoodleResults = async () => {
);
};
export const checkFinishedParticipants = async () => {
const participantsPending = await prisma.participant.findMany({
where: {
completetionWorkflowFinished: false,
},
include: {
Event: true,
User: true,
},
});
participantsPending.forEach(async (p) => {
if (!p.User) return;
const completed = eventCompleted(p.Event, p);
if (!completed) return;
console.log(
`User ${p.User.firstname} ${p.User.lastname} - ${p.User.publicId} finished event ${p.Event.name}`,
);
handleParticipantFinished(p.Event, p, p.User);
});
};
const checkUnfinishedParticipants = async () => {
const participantsPending = await prisma.participant.findMany({
where: {
completetionWorkflowFinished: false,
},
include: {
Event: true,
User: {
include: {
discordAccounts: true,
},
},
},
});
participantsPending.forEach(async (p) => {
if (!p.User) return;
const completed = eventCompleted(p.Event, p);
if (completed) return;
if (!p.Event.discordRoleId) {
await prisma.participant.update({
where: {
id: p.id,
},
data: {
inscriptionWorkflowCompleted: true,
},
});
return;
}
console.log(
`User ${p.User.firstname} ${p.User.lastname} - ${p.User.publicId} did not finish event ${p.Event.name}`,
);
if (p.User.discordAccounts[0] && p.Event.discordRoleId) {
await addRolesToMember(p.User.discordAccounts[0].discordId, [p.Event.discordRoleId]);
await prisma.participant.update({
where: {
id: p.id,
},
data: {
inscriptionWorkflowCompleted: true,
statusLog: {
push: {
event: "Discord-Rolle hinzugefügt",
timestamp: new Date(),
user: "system",
} as ParticipantLog as any,
},
},
});
}
});
};
const checkDiscordRoles = async () => {
const user = await prisma.user.findMany({
where: {
discordAccounts: {
some: {},
},
},
include: {
discordAccounts: true,
},
});
for (const u of user) {
// Here ony member Roles regarding their rights are checked
if (!u.discordAccounts[0]) continue;
const discordAccount = u.discordAccounts[0];
// For Pilot
if (u.permissions.includes("PILOT")) {
await addRolesToMember(discordAccount.discordId, [DISCORD_ROLES.PILOT]); // ONLINE_PILOT
} else {
await removeRolesFromMember(discordAccount.discordId, [DISCORD_ROLES.PILOT]); // ONLINE_PILOT
}
// for Dispatcher
if (u.permissions.includes("DISPO")) {
await addRolesToMember(discordAccount.discordId, [DISCORD_ROLES.DISPATCHER]); // ONLINE_DISPATCHER
} else {
await removeRolesFromMember(discordAccount.discordId, [DISCORD_ROLES.DISPATCHER]); // ONLINE_DISPATCHER
}
}
};
CronJob.from({ cronTime: "0 * * * *", onTick: syncMoodleIds, start: true });
CronJob.from({
cronTime: "*/1 * * * *",
onTick: async () => {
await updateParticipantMoodleResults();
await checkFinishedParticipants();
await checkUnfinishedParticipants();
},
start: true,
});
CronJob.from({
cronTime: "0 * * * *",
onTick: checkDiscordRoles,
start: true,
});

View File

@@ -36,3 +36,19 @@ export const removeRolesFromMember = async (memberId: string, roleIds: string[])
console.error("Error removing roles from member:", error);
});
};
export const setStandardName = async ({
memberId,
userId,
}: {
memberId: string;
userId: string;
}) => {
discordAxiosClient
.post("/helper/set-standard-name", {
memberId,
userId,
})
.catch((error) => {
console.error("Error removing roles from member:", error);
});
};

View File

@@ -1,5 +1,5 @@
import { Event, Participant, prisma, User } from "@repo/db";
import { removeRolesFromMember } from "modules/discord";
import { addRolesToMember, removeRolesFromMember, setStandardName } from "modules/discord";
import { sendCourseCompletedEmail } from "modules/mail";
export const handleParticipantFinished = async (
@@ -35,7 +35,10 @@ export const handleParticipantFinished = async (
});
if (event.discordRoleId && discordAccount) {
await removeRolesFromMember(discordAccount.discordId, [event.discordRoleId]);
await setStandardName({
memberId: discordAccount.discordId,
userId: user.id,
});
}
await sendCourseCompletedEmail(user.email, user, event);
@@ -55,3 +58,18 @@ export const handleParticipantFinished = async (
},
});
};
export const handleParticipantEnrolled = async (
event: Event,
participant: Participant,
user: User,
) => {
const discordAccount = await prisma.discordAccount.findFirst({
where: {
userId: user.id,
},
});
if (event.discordRoleId && discordAccount) {
await addRolesToMember(discordAccount.discordId, [event.discordRoleId]);
}
};

View File

@@ -0,0 +1,72 @@
import { prisma } from "@repo/db";
import { Router } from "express";
import { eventCompleted } from "helper/events";
import { handleParticipantEnrolled, handleParticipantFinished } from "modules/event";
const router: Router = Router();
router.post("/handle-participant-finished", async (req, res) => {
const { participantId } = req.body;
if (!participantId) {
res.status(400).json({ error: "Participant ID is required" });
return;
}
const participant = await prisma.participant.findUnique({
where: {
id: typeof participantId == "string" ? parseInt(participantId) : participantId,
},
include: {
Event: true,
User: {
include: {
discordAccounts: true,
},
},
},
});
if (!participant) {
res.status(404).json({ error: "Participant not found" });
return;
}
const completed = eventCompleted(participant.Event, participant);
if (!completed) {
res.status(400).json({ error: "Event not completed" });
return;
}
await handleParticipantFinished(participant.Event, participant, participant.User);
res.status(200).json({ message: "Participant finished successfully" });
});
router.post("/handle-participant-enrolled", async (req, res) => {
const { participantId } = req.body;
if (!participantId) {
res.status(400).json({ error: "Participant ID is required" });
return;
}
const participant = await prisma.participant.findUnique({
where: {
id: typeof participantId == "string" ? parseInt(participantId) : participantId,
},
include: {
Event: true,
User: {
include: {
discordAccounts: true,
},
},
},
});
if (!participant) {
res.status(404).json({ error: "Participant not found" });
return;
}
const completed = eventCompleted(participant.Event, participant);
if (completed) {
res.status(400).json({ error: "Event already completed" });
return;
}
await handleParticipantEnrolled(participant.Event, participant, participant.User);
res.status(200).json({ message: "Participant enrolled successfully" });
});
export default router;

View File

@@ -1,8 +1,10 @@
import { Router } from "express";
import mailRouter from "./mail";
import eventRouter from "./event";
const router: Router = Router();
router.use("/mail", mailRouter);
router.use("/event", eventRouter);
export default router;

View File

@@ -14,53 +14,32 @@ const page = async () => {
if (!user) return null;
const events = await prisma.event.findMany({
include: {
appointments: {
where: {
appointmentDate: {
gte: new Date(),
},
},
type: "OBLIGATED_COURSE",
},
include: {
participants: {
where: {
userId: user.id,
},
},
},
});
const userAppointments = await prisma.eventAppointment.findMany({
where: {
Participants: {
some: {
userId: user.id,
},
},
},
});
const appointments = await prisma.eventAppointment.findMany({
where: {
appointmentDate: {
gte: new Date(),
},
},
appointments: {
include: {
Participants: {
where: {
userId: user.id,
appointmentCancelled: false,
},
},
_count: {
select: {
Participants: true,
},
},
},
});
const filteredEvents = events.filter((event) => {
if (eventCompleted(event, event.participants[0])) return false;
const userParticipant = event.participants.find(
(participant) => participant.userId === user.id,
);
if (eventCompleted(event, userParticipant)) return false;
if (event.type === "OBLIGATED_COURSE" && !eventCompleted(event, event.participants[0]))
return true;
return false;
@@ -78,8 +57,10 @@ const page = async () => {
{filteredEvents.map((event) => {
return (
<KursItem
appointments={appointments}
selectedAppointments={userAppointments}
appointments={event.appointments}
selectedAppointments={event.appointments.filter((a) =>
a.Participants.find((p) => p.userId == user.id),
)}
user={user}
event={event}
key={event.id}

View File

@@ -9,6 +9,7 @@ import { Button } from "../../../../_components/ui/Button";
import { DateInput } from "../../../../_components/ui/DateInput";
import { upsertParticipant } from "../../../events/actions";
import { deleteAppoinement, upsertAppointment } from "../action";
import { handleParticipantFinished } from "../../../../../helper/events";
interface AppointmentModalProps {
event?: Event;
@@ -127,6 +128,9 @@ export const AppointmentModal = ({
attended: true,
appointmentCancelled: false,
});
if (!event.finisherMoodleCourseId) {
await handleParticipantFinished(row.original.id.toString());
}
participantTableRef.current?.refresh();
}}
className="btn btn-outline btn-info btn-sm"

View File

@@ -5,6 +5,9 @@ import { UseFormReturn } from "react-hook-form";
import { upsertParticipant } from "../../../events/actions";
import { RefObject } from "react";
import { deleteParticipant } from "../action";
import { handleParticipantFinished } from "../../../../../helper/events";
import { AxiosError } from "axios";
import toast from "react-hot-toast";
interface ParticipantModalProps {
participantForm: UseFormReturn<Participant>;
@@ -30,6 +33,27 @@ export const ParticipantModal = ({ participantForm, ref }: ParticipantModalProps
})}
className="space-y-1"
>
<Button
onClick={async () => {
if (!participantForm.watch("id")) return;
const participant = participantForm.getValues();
await handleParticipantFinished(participant.id.toString()).catch((e) => {
const error = e as AxiosError;
if (
error.response?.data &&
typeof error.response.data === "object" &&
"error" in error.response.data
) {
toast.error(`Fehler: ${error.response.data.error}`);
} else {
toast.error("Unbekannter Fehler beim ausführen des Workflows");
}
});
}}
>
Event-abgeschlossen Workflow ausführen
</Button>
<Switch form={participantForm} name="attended" label="Termin Teilgenommen" />
<Switch form={participantForm} name="appointmentCancelled" label="Termin abgesagt" />
<Switch

View File

@@ -4,6 +4,7 @@ import {
BADGES,
ConnectedAircraft,
ConnectedDispatcher,
DiscordAccount,
PERMISSION,
Report,
Station,
@@ -22,6 +23,7 @@ import {
LockOpen1Icon,
HobbyKnifeIcon,
ExclamationTriangleIcon,
DiscordLogoIcon,
} from "@radix-ui/react-icons";
import { Button } from "../../../../../_components/ui/Button";
import { Select } from "../../../../../_components/ui/Select";
@@ -34,6 +36,7 @@ import Link from "next/link";
import { ColumnDef } from "@tanstack/react-table";
import { Error } from "_components/Error";
import { useSession } from "next-auth/react";
import { setStandardName } from "../../../../../../helper/discord";
interface ProfileFormProps {
user: User;
@@ -362,6 +365,7 @@ export const UserReports = ({ user }: { user: User }) => {
};
interface AdminFormProps {
discordAccount?: DiscordAccount;
user: User;
dispoTime: {
hours: number;
@@ -380,7 +384,13 @@ interface AdminFormProps {
};
}
export const AdminForm = ({ user, dispoTime, pilotTime, reports }: AdminFormProps) => {
export const AdminForm = ({
user,
dispoTime,
pilotTime,
reports,
discordAccount,
}: AdminFormProps) => {
const router = useRouter();
return (
@@ -444,6 +454,27 @@ export const AdminForm = ({ user, dispoTime, pilotTime, reports }: AdminFormProp
<HobbyKnifeIcon /> User Entperren
</Button>
)}
{discordAccount && (
<div className="tooltip flex-1" data-tip={`Name: ${discordAccount.username}`}>
<Button
onClick={async () => {
await setStandardName({
memberId: discordAccount.discordId,
userId: user.id,
});
toast.success("Standard Name wurde gesetzt!", {
style: {
background: "var(--color-base-100)",
color: "var(--color-base-content)",
},
});
}}
className="btn-sm w-full btn-outline btn-info"
>
<DiscordLogoIcon /> Name und Berechtigungen setzen
</Button>
</div>
)}
</div>
</div>
<h2 className="card-title">

View File

@@ -1,15 +1,18 @@
import { PersonIcon } from "@radix-ui/react-icons";
import { prisma, User } from "@repo/db";
import { prisma } from "@repo/db";
import { AdminForm, ConnectionHistory, ProfileForm, UserReports } from "./_components/forms";
import { Error } from "../../../../_components/Error";
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const user: User | null = await prisma.user.findUnique({
const user = await prisma.user.findUnique({
where: {
id: id,
},
include: {
discordAccounts: true,
},
});
const dispoSessions = await prisma.connectedDispatcher.findMany({
@@ -88,7 +91,6 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
open: totalReportsOpen,
total60Days: totalReports60Days,
};
if (!user) return <Error statusCode={404} title="User not found" />;
return (
<div className="grid grid-cols-6 gap-4">
@@ -102,7 +104,13 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
<ProfileForm user={user} />
</div>
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
<AdminForm user={user} dispoTime={dispoTime} pilotTime={pilotTime} reports={reports} />
<AdminForm
user={user}
dispoTime={dispoTime}
pilotTime={pilotTime}
reports={reports}
discordAccount={user.discordAccounts[0]}
/>
</div>
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
<UserReports user={user} />

View File

@@ -1,5 +1,5 @@
"use client";
import { DrawingPinFilledIcon, EnterIcon } from "@radix-ui/react-icons";
import { DrawingPinFilledIcon } from "@radix-ui/react-icons";
import { Event, Participant, EventAppointment, User } from "@repo/db";
import ModalBtn from "./modalBtn";
import MDEditor from "@uiw/react-md-editor";
@@ -26,14 +26,10 @@ export const KursItem = ({
<h2 className="card-title">{event.name}</h2>
<div className="absolute top-0 right-0 m-4">
{event.type === "COURSE" && (
<span className="badge badge-info badge-outline">
Zusatzqualifikation
</span>
<span className="badge badge-info badge-outline">Zusatzqualifikation</span>
)}
{event.type === "OBLIGATED_COURSE" && (
<span className="badge badge-secondary badge-outline">
Verpflichtend
</span>
<span className="badge badge-secondary badge-outline">Verpflichtend</span>
)}
</div>
<div className="grid grid-cols-6 gap-4">
@@ -65,10 +61,7 @@ export const KursItem = ({
<b className="text-gray-600 text-left mr-2">Abzeichen:</b>
<div className="flex gap-2">
{event.requiredBadges.map((badge) => (
<div
className="badge badge-secondary badge-outline"
key={badge}
>
<div className="badge badge-secondary badge-outline" key={badge}>
{badge}
</div>
))}

View File

@@ -11,7 +11,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Select } from "../../../_components/ui/Select";
import toast from "react-hot-toast";
import { useRouter } from "next/navigation";
import { eventCompleted } from "../../../../helper/events";
import { eventCompleted, handleParticipantEnrolled } from "../../../../helper/events";
interface ModalBtnProps {
title: string;
@@ -123,13 +123,12 @@ const ModalBtn = ({
<div className="flex items-center gap-2 justify-center">
<CalendarIcon />
{!!dates.length && (
// TODO: Prevent users from selecting an appointment that is full
<Select
form={selectAppointmentForm as any}
form={selectAppointmentForm}
options={dates.map((date) => ({
label: `${new Date(
date.appointmentDate,
).toLocaleString()} - (${(date as any)._count.Participants}/${event.maxParticipants})`,
).toLocaleString()} - (${(date as any).Participants.length}/${event.maxParticipants})`,
value: date.id,
}))}
name="eventAppointmentId"
@@ -226,12 +225,14 @@ const ModalBtn = ({
const data = selectAppointmentForm.getValues();
if (!data.eventAppointmentId) return;
await upsertParticipant({
const participant = await upsertParticipant({
...data,
enscriptionDate: new Date(),
statusLog: data.statusLog?.filter((log) => log !== null),
appointmentCancelled: false,
});
await handleParticipantEnrolled(participant.id.toString());
router.refresh();
closeModal();
}}

View File

@@ -33,7 +33,11 @@ export default async function RootLayout({
>
<div className="hero-overlay bg-opacity-30"></div>
<div>
<Toaster position="top-center" reverseOrder={false} />
<Toaster
containerStyle={{ zIndex: "999999999" }}
position="top-center"
reverseOrder={false}
/>
</div>
{/* Card */}
<div className="hero-content text-neutral-content text-center w-full max-w-full h-full m-10">

View File

@@ -1,4 +1,4 @@
import Events from "./_components/Events";
import Events from "./_components/FeaturedEvents";
import { Stats } from "./_components/Stats";
import { Badges } from "./_components/Badges";
import { RecentFlights } from "(app)/_components/RecentFlights";

View File

@@ -1,18 +1,10 @@
import {
ButtonHTMLAttributes,
DetailedHTMLProps,
useEffect,
useState,
} from "react";
import { ButtonHTMLAttributes, DetailedHTMLProps, useEffect, useState } from "react";
import { cn } from "../../../helper/cn";
export const Button = ({
isLoading,
...props
}: DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> & {
}: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {
isLoading?: boolean;
}) => {
const [isLoadingState, setIsLoadingState] = useState(isLoading);
@@ -34,9 +26,7 @@ export const Button = ({
}
}}
>
{isLoadingState && (
<span className="loading loading-spinner loading-sm"></span>
)}
{isLoadingState && <span className="loading loading-spinner loading-sm"></span>}
{props.children as any}
</button>
);

View File

@@ -1,15 +1,15 @@
import axios, { AxiosError } from "axios";
import axios from "axios";
import { NextRequest, NextResponse } from "next/server";
import { DISCORD_ROLES, DiscordAccount, getPublicUser, prisma, PrismaClient } from "@repo/db";
import { DiscordAccount, prisma } from "@repo/db";
import { getServerSession } from "../auth/[...nextauth]/auth";
import { addRolesToMember, removeRolesFromMember, renameMember } from "../../../helper/discord";
import { setStandardName } from "../../../helper/discord";
export const GET = async (req: NextRequest) => {
const session = await getServerSession();
const code = req.nextUrl.searchParams.get("code");
if (!session) {
return NextResponse.redirect(`${process.env.NEXTAUTH_URL}/login`);
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_HUB_URL}/login`);
}
if (
@@ -72,17 +72,10 @@ export const GET = async (req: NextRequest) => {
where: { id: session.user.id },
});
if (user) {
await renameMember(discordUser.id, `${getPublicUser(user).fullName} - ${user?.publicId}`);
}
if (user?.permissions.includes("PILOT")) {
await addRolesToMember(discordUser.id, [DISCORD_ROLES.PILOT]);
} else {
await removeRolesFromMember(discordUser.id, [DISCORD_ROLES.PILOT]);
}
if (user?.permissions.includes("DISPO")) {
await addRolesToMember(discordUser.id, [DISCORD_ROLES.ONLINE_DISPATCHER]);
} else {
await removeRolesFromMember(discordUser.id, [DISCORD_ROLES.PILOT]);
await setStandardName({
memberId: discordUser.id,
userId: user.id,
});
}
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_HUB_URL}/settings`);

View File

@@ -37,3 +37,20 @@ export const removeRolesFromMember = async (memberId: string, roleIds: string[])
console.error("Error removing roles from member:", error);
});
};
export const setStandardName = async ({
memberId,
userId,
}: {
memberId: string;
userId: string;
}) => {
discordAxiosClient
.post("/helper/set-standard-name", {
memberId,
userId,
})
.catch((error) => {
console.error("Error removing roles from member:", error);
});
};

View File

@@ -1,4 +1,5 @@
import { Event, Participant } from "@repo/db";
import axios from "axios";
export const eventCompleted = (event: Event, participant?: Participant) => {
if (!participant) return false;
@@ -6,3 +7,13 @@ export const eventCompleted = (event: Event, participant?: Participant) => {
if (event.hasPresenceEvents && !participant.attended) return false;
return true;
};
export const handleParticipantFinished = async (participantId: string) =>
axios.post(`${process.env.NEXT_PUBLIC_HUB_SERVER_URL}/event/handle-participant-finished`, {
participantId,
});
export const handleParticipantEnrolled = async (participantId: string) =>
axios.post(`${process.env.NEXT_PUBLIC_HUB_SERVER_URL}/event/handle-participant-enrolled`, {
participantId,
});