formated project with prettier
This commit is contained in:
@@ -1,26 +1,13 @@
|
||||
import { prisma } from '@repo/db';
|
||||
import { Form } from '../_components/Form';
|
||||
import { prisma } from "@repo/db";
|
||||
import { Form } from "../_components/Form";
|
||||
|
||||
export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
||||
const { id } = await params;
|
||||
const event = await prisma.event.findUnique({
|
||||
where: {
|
||||
id: parseInt(id),
|
||||
},
|
||||
});
|
||||
const users = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
firstname: true,
|
||||
lastname: true,
|
||||
publicId: true,
|
||||
},
|
||||
});
|
||||
const appointments = await prisma.eventAppointment.findMany({
|
||||
where: {
|
||||
eventId: parseInt(id),
|
||||
},
|
||||
});
|
||||
if (!event) return <div>Event not found</div>;
|
||||
return <Form event={event} users={users} appointments={appointments} />;
|
||||
const { id } = await params;
|
||||
const event = await prisma.event.findUnique({
|
||||
where: {
|
||||
id: parseInt(id),
|
||||
},
|
||||
});
|
||||
if (!event) return <div>Event not found</div>;
|
||||
return <Form event={event} />;
|
||||
};
|
||||
|
||||
@@ -1,277 +1,264 @@
|
||||
'use client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
EventAppointmentOptionalDefaults,
|
||||
EventAppointmentOptionalDefaultsSchema,
|
||||
EventOptionalDefaults,
|
||||
EventOptionalDefaultsSchema,
|
||||
ParticipantOptionalDefaultsSchema,
|
||||
} from '@repo/db/zod';
|
||||
import { set, useForm } from 'react-hook-form';
|
||||
import { BADGES, Event, EventAppointment, User } from '@repo/db';
|
||||
import { Bot, Calendar, FileText, UserIcon } from 'lucide-react';
|
||||
import { Input } from '../../../../_components/ui/Input';
|
||||
import { useRef, useState } from 'react';
|
||||
import { deleteEvent, upsertAppointment, upsertEvent } from '../action';
|
||||
import { Button } from '../../../../_components/ui/Button';
|
||||
import { redirect, useRouter } from 'next/navigation';
|
||||
import { Switch } from '../../../../_components/ui/Switch';
|
||||
EventAppointmentOptionalDefaults,
|
||||
EventAppointmentOptionalDefaultsSchema,
|
||||
EventOptionalDefaults,
|
||||
EventOptionalDefaultsSchema,
|
||||
ParticipantOptionalDefaultsSchema,
|
||||
} from "@repo/db/zod";
|
||||
import { set, useForm } from "react-hook-form";
|
||||
import { BADGES, Event, EventAppointment, User } from "@repo/db";
|
||||
import { Bot, Calendar, FileText, UserIcon } from "lucide-react";
|
||||
import { Input } from "../../../../_components/ui/Input";
|
||||
import { useRef, useState } from "react";
|
||||
import { deleteEvent, upsertAppointment, upsertEvent } from "../action";
|
||||
import { Button } from "../../../../_components/ui/Button";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { Switch } from "../../../../_components/ui/Switch";
|
||||
import {
|
||||
PaginatedTable,
|
||||
PaginatedTableRef,
|
||||
} from '../../../../_components/PaginatedTable';
|
||||
import { Select } from '../../../../_components/ui/Select';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { MarkdownEditor } from '../../../../_components/ui/MDEditor';
|
||||
PaginatedTable,
|
||||
PaginatedTableRef,
|
||||
} from "../../../../_components/PaginatedTable";
|
||||
import { Select } from "../../../../_components/ui/Select";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { MarkdownEditor } from "../../../../_components/ui/MDEditor";
|
||||
|
||||
export const Form = ({
|
||||
event,
|
||||
users,
|
||||
appointments = [],
|
||||
}: {
|
||||
event?: Event;
|
||||
users: {
|
||||
id: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
publicId: string;
|
||||
}[];
|
||||
appointments?: EventAppointment[];
|
||||
}) => {
|
||||
const { data: session } = useSession();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||
defaultValues: event,
|
||||
});
|
||||
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
||||
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||
defaultValues: {
|
||||
eventId: event?.id,
|
||||
presenterId: session?.user?.id,
|
||||
},
|
||||
});
|
||||
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
||||
return (
|
||||
<>
|
||||
<dialog ref={addParticipantModal} 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>
|
||||
</form>
|
||||
<h3 className="font-bold text-lg">Termin hinzufügen</h3>
|
||||
<form
|
||||
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||
if (!event) return;
|
||||
const createdAppointment = await upsertAppointment({
|
||||
appointmentDate: values.appointmentDate,
|
||||
eventId: event?.id,
|
||||
presenterId: values.presenterId,
|
||||
});
|
||||
console.log(createdAppointment);
|
||||
addParticipantModal.current?.close();
|
||||
appointmentsTableRef.current?.refresh();
|
||||
})}
|
||||
className="flex flex-col"
|
||||
>
|
||||
<Input
|
||||
form={appointmentForm}
|
||||
label="Datum"
|
||||
name="appointmentDate"
|
||||
type="date"
|
||||
/>
|
||||
<div className="modal-action">
|
||||
<Button type="submit" className="btn btn-primary">
|
||||
Hinzufügen
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setLoading(true);
|
||||
const createdEvent = await upsertEvent(values, event?.id);
|
||||
setLoading(false);
|
||||
if (!event) redirect(`/admin/event`);
|
||||
})}
|
||||
className="grid grid-cols-6 gap-3"
|
||||
>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<FileText className="w-5 h-5" /> Allgemeines
|
||||
</h2>
|
||||
<Input form={form} label="Name" name="name" className="input-sm" />
|
||||
<MarkdownEditor form={form} name="description" />
|
||||
<Input
|
||||
form={form}
|
||||
label="Maximale Teilnehmer (Nur für live Events)"
|
||||
className="input-sm"
|
||||
{...form.register('maxParticipants', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Switch form={form} name="hidden" label="Versteckt" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<Bot className="w-5 h-5" /> Automation
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
name="starterMoodleCourseId"
|
||||
label="Moodle Anmelde Kurs ID"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
name="finisherMoodleCourseId"
|
||||
form={form}
|
||||
label="Moodle Abschluss Kurs ID"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Select
|
||||
isMulti
|
||||
form={form}
|
||||
name="finishedBadges"
|
||||
label="Badges bei Abschluss"
|
||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||
label: value,
|
||||
value: key,
|
||||
}))}
|
||||
/>
|
||||
<Select
|
||||
isMulti
|
||||
form={form}
|
||||
name="requiredBadges"
|
||||
label="Benötigte Badges"
|
||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||
label: value,
|
||||
value: key,
|
||||
}))}
|
||||
/>
|
||||
<Switch
|
||||
form={form}
|
||||
name="hasPresenceEvents"
|
||||
label="Hat Live Event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="card-title">
|
||||
<Calendar className="w-5 h-5" /> Termine
|
||||
</h2>
|
||||
{event && (
|
||||
<button
|
||||
className="btn btn-primary btn-outline"
|
||||
onClick={() => addParticipantModal.current?.showModal()}
|
||||
>
|
||||
Hinzufügen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
export const Form = ({ event }: { event?: Event }) => {
|
||||
const { data: session } = useSession();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||
defaultValues: event,
|
||||
});
|
||||
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
||||
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||
defaultValues: {
|
||||
eventId: event?.id,
|
||||
presenterId: session?.user?.id,
|
||||
},
|
||||
});
|
||||
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
||||
return (
|
||||
<>
|
||||
<dialog ref={addParticipantModal} 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>
|
||||
</form>
|
||||
<h3 className="font-bold text-lg">Termin hinzufügen</h3>
|
||||
<form
|
||||
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||
if (!event) return;
|
||||
const createdAppointment = await upsertAppointment({
|
||||
appointmentDate: values.appointmentDate,
|
||||
eventId: event?.id,
|
||||
presenterId: values.presenterId,
|
||||
});
|
||||
console.log(createdAppointment);
|
||||
addParticipantModal.current?.close();
|
||||
appointmentsTableRef.current?.refresh();
|
||||
})}
|
||||
className="flex flex-col"
|
||||
>
|
||||
<Input
|
||||
form={appointmentForm}
|
||||
label="Datum"
|
||||
name="appointmentDate"
|
||||
type="date"
|
||||
/>
|
||||
<div className="modal-action">
|
||||
<Button type="submit" className="btn btn-primary">
|
||||
Hinzufügen
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setLoading(true);
|
||||
const createdEvent = await upsertEvent(values, event?.id);
|
||||
setLoading(false);
|
||||
if (!event) redirect(`/admin/event`);
|
||||
})}
|
||||
className="grid grid-cols-6 gap-3"
|
||||
>
|
||||
<div className="card bg-base-200 shadow-xl col-span-3 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<FileText className="w-5 h-5" /> Allgemeines
|
||||
</h2>
|
||||
<Input form={form} label="Name" name="name" className="input-sm" />
|
||||
<MarkdownEditor form={form} name="description" />
|
||||
<Input
|
||||
form={form}
|
||||
label="Maximale Teilnehmer (Nur für live Events)"
|
||||
className="input-sm"
|
||||
{...form.register("maxParticipants", {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Switch form={form} name="hidden" label="Versteckt" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-3 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<Bot className="w-5 h-5" /> Automation
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
name="starterMoodleCourseId"
|
||||
label="Moodle Anmelde Kurs ID"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
name="finisherMoodleCourseId"
|
||||
form={form}
|
||||
label="Moodle Abschluss Kurs ID"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Select
|
||||
isMulti
|
||||
form={form}
|
||||
name="finishedBadges"
|
||||
label="Badges bei Abschluss"
|
||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||
label: value,
|
||||
value: key,
|
||||
}))}
|
||||
/>
|
||||
<Select
|
||||
isMulti
|
||||
form={form}
|
||||
name="requiredBadges"
|
||||
label="Benötigte Badges"
|
||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||
label: value,
|
||||
value: key,
|
||||
}))}
|
||||
/>
|
||||
<Switch
|
||||
form={form}
|
||||
name="hasPresenceEvents"
|
||||
label="Hat Live Event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="card-title">
|
||||
<Calendar className="w-5 h-5" /> Termine
|
||||
</h2>
|
||||
{event && (
|
||||
<button
|
||||
className="btn btn-primary btn-outline"
|
||||
onClick={() => addParticipantModal.current?.showModal()}
|
||||
>
|
||||
Hinzufügen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<PaginatedTable
|
||||
ref={appointmentsTableRef}
|
||||
prismaModel={'eventAppointment'}
|
||||
filter={{
|
||||
eventId: event?.id,
|
||||
}}
|
||||
include={{
|
||||
Presenter: true,
|
||||
Participants: true,
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
header: 'Datum',
|
||||
accessorKey: 'appointmentDate',
|
||||
accessorFn: (date) =>
|
||||
new Date(date.appointmentDate).toLocaleDateString(),
|
||||
},
|
||||
{
|
||||
header: 'Presenter',
|
||||
accessorKey: 'presenter',
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">
|
||||
{(row.original as any).Presenter.firstname}{' '}
|
||||
{(row.original as any).Presenter.lastname}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Teilnehmer',
|
||||
accessorKey: 'Participants',
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center">
|
||||
<UserIcon className="w-5 h-5" />
|
||||
<span className="ml-2">
|
||||
{row.original.Participants.length}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Aktionen',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onSubmit={() => false}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
console.log(row.original);
|
||||
// TODO: open modal to edit appointment
|
||||
}}
|
||||
className="btn btn-sm btn-outline"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||
<div className="card-body ">
|
||||
<div className="flex w-full gap-4">
|
||||
<Button
|
||||
isLoading={loading}
|
||||
type="submit"
|
||||
className="btn btn-primary flex-1"
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
{event && (
|
||||
<Button
|
||||
isLoading={deleteLoading}
|
||||
onClick={async () => {
|
||||
setDeleteLoading(true);
|
||||
await deleteEvent(event.id);
|
||||
redirect('/admin/event');
|
||||
}}
|
||||
className="btn btn-error"
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
<PaginatedTable
|
||||
ref={appointmentsTableRef}
|
||||
prismaModel={"eventAppointment"}
|
||||
filter={{
|
||||
eventId: event?.id,
|
||||
}}
|
||||
include={{
|
||||
Presenter: true,
|
||||
Participants: true,
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
header: "Datum",
|
||||
accessorKey: "appointmentDate",
|
||||
accessorFn: (date) =>
|
||||
new Date(date.appointmentDate).toLocaleDateString(),
|
||||
},
|
||||
{
|
||||
header: "Presenter",
|
||||
accessorKey: "presenter",
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">
|
||||
{(row.original as any).Presenter.firstname}{" "}
|
||||
{(row.original as any).Presenter.lastname}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Teilnehmer",
|
||||
accessorKey: "Participants",
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center">
|
||||
<UserIcon className="w-5 h-5" />
|
||||
<span className="ml-2">
|
||||
{row.original.Participants.length}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Aktionen",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onSubmit={() => false}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
console.log(row.original);
|
||||
// TODO: open modal to edit appointment
|
||||
}}
|
||||
className="btn btn-sm btn-outline"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||
<div className="card-body ">
|
||||
<div className="flex w-full gap-4">
|
||||
<Button
|
||||
isLoading={loading}
|
||||
type="submit"
|
||||
className="btn btn-primary flex-1"
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
{event && (
|
||||
<Button
|
||||
isLoading={deleteLoading}
|
||||
onClick={async () => {
|
||||
setDeleteLoading(true);
|
||||
await deleteEvent(event.id);
|
||||
redirect("/admin/event");
|
||||
}}
|
||||
className="btn btn-error"
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { prisma } from '@repo/db';
|
||||
import { Form } from '../_components/Form';
|
||||
import { prisma } from "@repo/db";
|
||||
import { Form } from "../_components/Form";
|
||||
|
||||
export default async () => {
|
||||
const users = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
firstname: true,
|
||||
lastname: true,
|
||||
publicId: true,
|
||||
},
|
||||
});
|
||||
return <Form users={users} />;
|
||||
return <Form />;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user