-
-
Einsteigerkurs für Piloten
-
-
- Verpflichtend
-
-
-
-
-
- In diesem Kurs lernen Piloten die Grundlagen der Luftrettung,
- Einsatzverfahren, den Umgang mit dem BOS-Funk und einige
- medizinische Basics. Der Kurs bietet eine ideale Vorbereitung
- für alle Standard Operations bei Virtual Air Rescue.
-
-
-
Badge
-
-
-
- Teilnahmevoraussetzungen: Keine
-
-
diff --git a/apps/hub/app/(app)/events/_components/modalBtn.tsx b/apps/hub/app/(app)/events/_components/modalBtn.tsx
index cd31abe0..8962a574 100644
--- a/apps/hub/app/(app)/events/_components/modalBtn.tsx
+++ b/apps/hub/app/(app)/events/_components/modalBtn.tsx
@@ -7,9 +7,21 @@ import {
} from "@radix-ui/react-icons";
import { Event, EventAppointment, Participant, User } from "@repo/db";
import { cn } from "../../../../helper/cn";
-import { addParticipant, inscribeToMoodleCourse } from "../actions";
+import { inscribeToMoodleCourse, upsertParticipant } from "../actions";
import { useSession } from "next-auth/react";
-import { Cross } from "lucide-react";
+import { Clock10Icon, Cross } from "lucide-react";
+import { useForm } from "react-hook-form";
+import {
+ EventAppointmentOptionalDefaults,
+ EventAppointmentSchema,
+ ParticipantOptionalDefaults,
+ ParticipantOptionalDefaultsSchema,
+ ParticipantSchema,
+} from "@repo/db/zod";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Select } from "../../../_components/ui/Select";
+import toast from "react-hot-toast";
+import { useRouter } from "next/navigation";
interface ModalBtnProps {
title: string;
@@ -45,6 +57,7 @@ const ModalBtn = ({
modal?.removeEventListener("close", handleClose);
};
}, [modalId]);
+ const router = useRouter();
const canSelectDate =
event.hasPresenceEvents &&
@@ -62,7 +75,15 @@ const ModalBtn = ({
document.body.classList.remove("modal-open");
modal?.close();
};
-
+ const selectAppointmentForm = useForm
({
+ resolver: zodResolver(ParticipantOptionalDefaultsSchema),
+ defaultValues: {
+ eventId: event.id,
+ userId: user.id,
+ ...participant,
+ },
+ });
+ const selectedAppointment = selectedAppointments[0];
return (
<>
{!!dates.length && (
-
- Bitte wähle einen Termin aus
- {dates.map((date, index) => (
-
- {date.appointmentDate.toLocaleString()}
-
- ))}
-
+ ({
+ label: new Date(date.appointmentDate).toLocaleString(),
+ value: date.id,
+ }))}
+ name="eventAppointmentId"
+ label={""}
+ className="min-w-[200px]"
+ />
)}
{!dates.length && (
@@ -99,6 +122,31 @@ const ModalBtn = ({
)}
)}
+ {selectedAppointment && !participant?.appointmentCancelled && (
+
+
Dein Ausgewähler Termin
+
+
+ {new Date(
+ selectedAppointment.appointmentDate,
+ ).toLocaleString()}
+
+
{
+ await upsertParticipant({
+ eventId: event.id,
+ userId: user.id,
+ appointmentCancelled: true,
+ });
+ toast.success("Termin abgesagt");
+ router.refresh();
+ }}
+ className="btn btn-error btn-outline btn-sm"
+ >
+ absagen
+
+
+ )}
{!!dates.length && (
Bitte finde dich an diesem Termin in unserem Discord ein.
@@ -111,7 +159,7 @@ const ModalBtn = ({
participant={participant}
user={user}
moodleCourseId={event.finisherMoodleCourseId}
- completed={participant?.finisherMoodleCurseCompleted}
+ completed={participant?.finisherMoodleCurseCompleted || false}
event={event}
/>
)}
@@ -119,8 +167,27 @@ const ModalBtn = ({
Abbrechen
- {!!(event.hasPresenceEvents && dates.length) && (
-
+ {!!canSelectDate && (
+ {
+ console.log("submit", selectAppointmentForm.formState.errors);
+ const data = selectAppointmentForm.getValues();
+ if (!data.eventAppointmentId) return;
+
+ console.log("submit", data);
+ await upsertParticipant({
+ ...data,
+ statusLog: data.statusLog?.filter((log) => log !== null),
+ appointmentCancelled: false,
+ });
+ router.refresh();
+ closeModal();
+ }}
+ >
Anmelden
)}
@@ -150,6 +217,13 @@ const MoodleCourseIndicator = ({
event: Event;
}) => {
const courseUrl = `${process.env.NEXT_PUBLIC_MOODLE_URL}/course/view.php?id=${moodleCourseId}`;
+ if (!participant || (event.hasPresenceEvents && !participant?.attended))
+ return (
+
+
+ Abschlusstest erst nach Teilnahme verfügbar
+
+ );
if (completed)
return (
@@ -157,20 +231,17 @@ const MoodleCourseIndicator = ({
Moodle Kurs abgeschlossen
);
- if (!participant || (event.hasPresenceEvents && !participant?.attended))
- return (
-
-
- Teilnahme an Event erforderlich
-
- );
return (
Moodle-Kurs Benötigt
{
- await addParticipant(event.id, user.id);
+ await upsertParticipant({
+ eventId: event.id,
+ userId: user.id,
+ finisherMoodleCurseCompleted: false,
+ });
if (user.moodleId) {
await inscribeToMoodleCourse(moodleCourseId, user.moodleId);
diff --git a/apps/hub/app/(app)/events/actions.ts b/apps/hub/app/(app)/events/actions.ts
index a235d770..73921e16 100644
--- a/apps/hub/app/(app)/events/actions.ts
+++ b/apps/hub/app/(app)/events/actions.ts
@@ -1,6 +1,6 @@
"use server";
import { enrollUserInCourse } from "../../../helper/moodle";
-import { prisma } from "@repo/db";
+import { Prisma, prisma } from "@repo/db";
export const inscribeToMoodleCourse = async (
moodleCourseId: string | number,
@@ -9,18 +9,24 @@ export const inscribeToMoodleCourse = async (
await enrollUserInCourse(moodleCourseId, userMoodleId);
};
-export const addParticipant = async (eventId: number, userId: string) => {
+export const upsertParticipant = async (
+ data: Prisma.ParticipantUncheckedCreateInput,
+) => {
const participant = await prisma.participant.findFirst({
where: {
- userId: userId,
+ userId: data.userId,
+ eventId: data.eventId,
},
});
if (!participant) {
- await prisma.participant.create({
- data: {
- userId: userId,
- eventId,
- },
+ return await prisma.participant.create({
+ data,
});
}
+ return await prisma.participant.update({
+ where: {
+ id: participant.id,
+ },
+ data,
+ });
};
diff --git a/apps/hub/app/(app)/events/page.tsx b/apps/hub/app/(app)/events/page.tsx
index 71ffb447..0c6f8a93 100644
--- a/apps/hub/app/(app)/events/page.tsx
+++ b/apps/hub/app/(app)/events/page.tsx
@@ -1,6 +1,6 @@
import { getServerSession } from "../../api/auth/[...nextauth]/auth";
import { PrismaClient } from "@repo/db";
-import { ObligatedEvent, KursItem } from "./_components/item";
+import { KursItem } from "./_components/item";
import { RocketIcon } from "@radix-ui/react-icons";
export default async () => {
@@ -49,24 +49,14 @@ export default async () => {
{events.map((event) => {
- if (event.type === "OBLIGATED_COURSE")
- return (
-