CHanged Event admin layout

This commit is contained in:
PxlLoewe
2025-03-10 18:11:28 -07:00
parent 77b266e0bf
commit c01e618a56
17 changed files with 918 additions and 170 deletions

View File

@@ -22,33 +22,37 @@ import { Switch } from "../../../../_components/ui/Switch";
interface AppointmentModalProps {
event?: Event;
ref: RefObject<HTMLDialogElement | null>;
participantModal: RefObject<HTMLDialogElement | null>;
appointmentsTableRef: React.RefObject<PaginatedTableRef>;
appointmentForm: UseFormReturn<
EventAppointmentOptionalDefaults,
any,
undefined
>;
participantForm: UseFormReturn<Participant, any, undefined>;
}
export const AppointmentModal = ({
event,
ref,
participantModal,
appointmentsTableRef,
appointmentForm,
participantForm,
}: AppointmentModalProps) => {
const { data: session } = useSession();
const participantTableRef = useRef<PaginatedTableRef>(null);
const participantForm = useForm<Participant>({
resolver: zodResolver(ParticipantOptionalDefaultsSchema),
});
return (
<dialog ref={ref} className="modal">
<div className="modal-box">
<form method="dialog">
{/* if there is a button in form, it will close the modal */}
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
<button
className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
onClick={() => ref.current?.close()}
>
</button>
</form>
@@ -57,6 +61,7 @@ export const AppointmentModal = ({
</h3>
<form
onSubmit={appointmentForm.handleSubmit(async (values) => {
console.log(values);
if (!event) return;
const createdAppointment = await upsertAppointment(values);
ref.current?.close();
@@ -75,131 +80,83 @@ export const AppointmentModal = ({
}}
/> */}
<div>
{appointmentForm.watch("id") && (
<PaginatedTable
ref={participantTableRef}
columns={[
{
accessorKey: "User.firstname",
header: "Vorname",
},
{
accessorKey: "User.lastname",
header: "Nachname",
},
{
header: "Aktion",
cell: ({ row }: CellContext<Participant, any>) => {
return (
<>
<PaginatedTable
hide={appointmentForm.watch("id") === undefined}
ref={participantTableRef}
columns={[
{
accessorKey: "User.firstname",
header: "Vorname",
},
{
accessorKey: "User.lastname",
header: "Nachname",
},
{
header: "Aktion",
cell: ({ row }: CellContext<Participant, any>) => {
return (
<>
<button
onClick={() => {
participantForm.reset(row.original);
participantModal.current?.showModal();
}}
className="btn btn-outline btn-sm"
>
anzeigen
</button>
{!row.original.attended && event?.hasPresenceEvents && (
<button
onClick={() => {
participantForm.reset(row.original);
type="button"
onSubmit={() => {}}
onClick={async () => {
await upsertParticipant({
eventId: event!.id,
userId: participantForm.watch("userId"),
attended: true,
});
participantTableRef.current?.refresh();
}}
className="btn btn-outline btn-sm"
className="btn btn-outline btn-info btn-sm"
>
anzeigen
Anwesend
</button>
{!row.original.attended &&
event?.hasPresenceEvents && (
<button
type="button"
onSubmit={() => {}}
onClick={async () => {
await upsertParticipant({
eventId: event!.id,
userId: participantForm.watch("userId"),
attended: true,
});
participantTableRef.current?.refresh();
}}
className="btn btn-outline btn-info btn-sm"
>
Anwesend
</button>
)}
</>
);
},
)}
</>
);
},
]}
prismaModel={"participant"}
filter={{
eventAppointmentId: appointmentForm.watch("id"),
}}
include={{ User: true }}
leftOfPagination={
<div className="flex gap-2">
<Button type="submit" className="btn btn-primary">
Speichern
},
]}
prismaModel={"participant"}
filter={{
eventAppointmentId: appointmentForm.watch("id"),
}}
include={{ User: true }}
leftOfPagination={
<div className="flex gap-2">
<Button type="submit" className="btn btn-primary">
Speichern
</Button>
{appointmentForm.watch("id") && (
<Button
type="button"
onSubmit={() => {}}
onClick={async () => {
await deleteAppoinement(appointmentForm.watch("id")!);
ref.current?.close();
appointmentsTableRef.current?.refresh();
}}
className="btn btn-error btn-outline"
>
Löschen
</Button>
{appointmentForm.watch("id") && (
<Button
type="button"
onSubmit={() => {}}
onClick={async () => {
await deleteAppoinement(appointmentForm.watch("id")!);
ref.current?.close();
appointmentsTableRef.current?.refresh();
}}
className="btn btn-error btn-outline"
>
Löschen
</Button>
)}
</div>
}
/>
)}
)}
</div>
}
/>
</div>
<div className="modal-action"></div>
</form>
{participantForm.watch("id") && (
<form
onSubmit={participantForm.handleSubmit(async (data) => {
await upsertParticipant({
...data,
statusLog: data.statusLog as Prisma.InputJsonValue[],
});
participantTableRef.current?.refresh();
})}
className="space-y-1"
>
<h1 className="text-2xl">Teilnehmer bearbeiten</h1>
<Switch
form={participantForm}
name="appointmentCancelled"
label="Termin abgesagt"
/>
<Switch
form={participantForm}
name="finisherMoodleCurseCompleted"
label="Abschluss-Moodle kurs abgeschlossen"
/>
{event?.hasPresenceEvents && (
<Switch
form={participantForm}
name="attended"
label="An Presenstermin teilgenommen"
/>
)}
<div className="flex flex-col">
<h3 className="text-xl">Verlauf</h3>
{participantForm.watch("statusLog").map((s) => {
return (
<div
className="flex justify-between"
key={(s as any).timestamp}
>
<p>{(s as any).event}</p>
<p>{new Date((s as any).timestamp).toLocaleString()}</p>
</div>
);
})}
</div>
<Button>Speichern</Button>
</form>
)}
</div>
</dialog>
);

View File

@@ -17,7 +17,7 @@ import {
prisma,
Prisma,
} from "@repo/db";
import { Bot, Calendar, FileText, UserIcon } from "lucide-react";
import { Bot, Calendar, FileText, User, UserIcon } from "lucide-react";
import { Input } from "../../../../_components/ui/Input";
import { useRef, useState } from "react";
import {
@@ -39,6 +39,7 @@ import { Select } from "../../../../_components/ui/Select";
import { useSession } from "next-auth/react";
import { MarkdownEditor } from "../../../../_components/ui/MDEditor";
import { AppointmentModal } from "./AppointmentModal";
import { ParticipantModal } from "./ParticipantModal";
export const Form = ({ event }: { event?: Event }) => {
const { data: session } = useSession();
@@ -53,17 +54,28 @@ export const Form = ({ event }: { event?: Event }) => {
presenterId: session?.user?.id,
},
});
const participantForm = useForm<Participant>({
resolver: zodResolver(ParticipantOptionalDefaultsSchema),
});
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
const [loading, setLoading] = useState(false);
const [deleteLoading, setDeleteLoading] = useState(false);
const appointmentModal = useRef<HTMLDialogElement>(null);
const participantModal = useRef<HTMLDialogElement>(null);
return (
<>
<AppointmentModal
participantModal={participantModal}
participantForm={participantForm}
appointmentForm={appointmentForm}
ref={appointmentModal}
appointmentsTableRef={appointmentsTableRef}
event={event}
/>
<ParticipantModal
participantForm={participantForm}
ref={participantModal}
/>
<form
onSubmit={form.handleSubmit(async (values) => {
@@ -164,6 +176,8 @@ export const Form = ({ event }: { event?: Event }) => {
appointmentModal.current?.showModal();
appointmentForm.reset({
id: undefined,
eventId: event.id,
presenterId: session?.user?.id,
});
}}
>
@@ -238,6 +252,62 @@ export const Form = ({ event }: { event?: Event }) => {
</div>
</div>
) : null}
{!form.watch("hasPresenceEvents") ? (
<div className="card bg-base-200 shadow-xl col-span-6">
<div className="card-body">
<PaginatedTable
leftOfSearch={
<h2 className="card-title">
<Calendar className="w-5 h-5" /> Teilnehmer
</h2>
}
searchFields={["User.firstname", "User.lastname"]}
ref={appointmentsTableRef}
prismaModel={"participant"}
filter={{
eventId: event?.id,
}}
include={{
User: true,
}}
columns={[
{
header: "Vorname",
accessorKey: "User.firstname",
},
{
header: "Nachname",
accessorKey: "User.lastname",
},
{
header: "Moodle Kurs abgeschlossen",
accessorKey: "finisherMoodleCurseCompleted",
},
{
header: "Aktionen",
cell: ({ row }) => {
return (
<div className="flex gap-2">
<button
onSubmit={() => false}
type="button"
onClick={() => {
participantForm.reset(row.original);
participantModal.current?.showModal();
}}
className="btn btn-sm btn-outline"
>
Bearbeiten
</button>
</div>
);
},
},
]}
/>
</div>
</div>
) : null}
<div className="card bg-base-200 shadow-xl col-span-6">
<div className="card-body ">
<div className="flex w-full gap-4">

View File

@@ -0,0 +1,66 @@
import { Switch } from "../../../../_components/ui/Switch";
import { Button } from "../../../../_components/ui/Button";
import { Participant, Prisma } from "@repo/db";
import { UseFormReturn } from "react-hook-form";
import { upsertParticipant } from "../../../events/actions";
import { RefObject } from "react";
interface ParticipantModalProps {
participantForm: UseFormReturn<Participant>;
ref: RefObject<HTMLDialogElement | null>;
}
export const ParticipantModal = ({
participantForm,
ref,
}: ParticipantModalProps) => {
return (
<dialog className="modal" ref={ref}>
<div className="modal-box">
<form method="dialog">
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
</button>
</form>
<h1 className="text-2xl">Teilnehmer bearbeiten</h1>
<form
onSubmit={participantForm.handleSubmit(async (data) => {
await upsertParticipant({
...data,
statusLog: data.statusLog as Prisma.InputJsonValue[],
});
participantForm.reset();
ref.current?.close();
})}
className="space-y-1"
>
<Switch
form={participantForm}
name="appointmentCancelled"
label="Termin abgesagt"
/>
<Switch
form={participantForm}
name="finisherMoodleCurseCompleted"
label="Abschluss-Moodle-Kurs abgeschlossen"
/>
<Switch
form={participantForm}
name="completetionWorkflowFinished"
label="Abgeschlossen (E-Mail-Benachrichtigung senden)"
/>
<div className="flex flex-col">
<h3 className="text-xl">Verlauf</h3>
{participantForm.watch("statusLog")?.map((s) => (
<div className="flex justify-between" key={(s as any).timestamp}>
<p>{(s as any).event}</p>
<p>{new Date((s as any).timestamp).toLocaleString()}</p>
</div>
))}
</div>
<Button type="submit">Speichern</Button>
</form>
</div>
</dialog>
);
};