added event appointments
This commit is contained in:
@@ -8,6 +8,14 @@ export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
id: parseInt(id),
|
id: parseInt(id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
firstname: true,
|
||||||
|
lastname: true,
|
||||||
|
publicId: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
if (!event) return <div>Event not found</div>;
|
if (!event) return <div>Event not found</div>;
|
||||||
return <Form event={event} />;
|
return <Form event={event} users={users} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import {
|
import {
|
||||||
|
EventAppointmentOptionalDefaultsSchema,
|
||||||
EventOptionalDefaults,
|
EventOptionalDefaults,
|
||||||
EventOptionalDefaultsSchema,
|
EventOptionalDefaultsSchema,
|
||||||
ParticipantOptionalDefaultsSchema,
|
ParticipantOptionalDefaultsSchema,
|
||||||
} from '@repo/db/zod';
|
} from '@repo/db/zod';
|
||||||
import { set, useForm } from 'react-hook-form';
|
import { set, useForm } from 'react-hook-form';
|
||||||
import { BADGES, Event } from '@repo/db';
|
import { BADGES, Event, User } from '@repo/db';
|
||||||
import { Bot, FileText, UserIcon } from 'lucide-react';
|
import { Bot, FileText, UserIcon } from 'lucide-react';
|
||||||
import { Input } from '../../../../_components/ui/Input';
|
import { Input } from '../../../../_components/ui/Input';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
@@ -17,15 +18,26 @@ import { Switch } from '../../../../_components/ui/Switch';
|
|||||||
import { PaginatedTable } from '../../../../_components/PaginatedTable';
|
import { PaginatedTable } from '../../../../_components/PaginatedTable';
|
||||||
import { Select } from '../../../../_components/ui/Select';
|
import { Select } from '../../../../_components/ui/Select';
|
||||||
|
|
||||||
export const Form = ({ event }: { event?: Event }) => {
|
export const Form = ({
|
||||||
|
event,
|
||||||
|
users,
|
||||||
|
}: {
|
||||||
|
event?: Event;
|
||||||
|
users: {
|
||||||
|
id: string;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
publicId: string;
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||||
defaultValues: event,
|
defaultValues: event,
|
||||||
});
|
});
|
||||||
const participantForm = useForm({
|
const appointmentForm = useForm({
|
||||||
resolver: zodResolver(ParticipantOptionalDefaultsSchema),
|
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||||
});
|
});
|
||||||
|
console.log(appointmentForm.formState.errors);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
||||||
@@ -33,13 +45,33 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
<>
|
<>
|
||||||
<dialog ref={addParticipantModal} className="modal">
|
<dialog ref={addParticipantModal} className="modal">
|
||||||
<div className="modal-box">
|
<div className="modal-box">
|
||||||
<h3 className="font-bold text-lg">Teilnehmer</h3>
|
<form method="dialog">
|
||||||
<div className="modal-action">
|
{/* if there is a button in form, it will close the modal */}
|
||||||
<form method="dialog">
|
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
|
||||||
{/* if there is a button in form, it will close the modal */}
|
✕
|
||||||
<button className="btn">Close</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
<h3 className="font-bold text-lg">Teilnehmer hinzufügen</h3>
|
||||||
|
<form
|
||||||
|
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||||
|
console.log(values);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
form={appointmentForm}
|
||||||
|
name="userId"
|
||||||
|
label="Teilnehmer"
|
||||||
|
options={users.map((user) => ({
|
||||||
|
label: `${user.firstname} ${user.lastname} (${user.publicId})`,
|
||||||
|
value: user.id,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
<div className="modal-action">
|
||||||
|
<Button type="submit" className="btn btn-primary">
|
||||||
|
Hinzufügen
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
<form
|
<form
|
||||||
@@ -122,18 +154,18 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h2 className="card-title">
|
<h2 className="card-title">
|
||||||
<UserIcon className="w-5 h-5" /> Teilnehmer
|
<UserIcon className="w-5 h-5" /> Termine
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-outline"
|
className="btn btn-primary btn-outline"
|
||||||
onClick={() => addParticipantModal.current?.showModal()}
|
onClick={() => addParticipantModal.current?.showModal()}
|
||||||
>
|
>
|
||||||
Teilnehmer hinzufügen
|
Hinzufügen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
prismaModel={'participant'}
|
prismaModel={'eventAppointment'}
|
||||||
filter={{
|
filter={{
|
||||||
eventId: event?.id,
|
eventId: event?.id,
|
||||||
}}
|
}}
|
||||||
@@ -142,24 +174,7 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
user: true,
|
user: true,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
columns={[
|
columns={[]}
|
||||||
{
|
|
||||||
header: 'Vorname',
|
|
||||||
accessorKey: 'user.firstName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Nachname',
|
|
||||||
accessorKey: 'user.lastname',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'VAR ID',
|
|
||||||
accessorKey: 'user.publicId',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Status',
|
|
||||||
accessorKey: 'status',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ const SelectCom = <T extends FieldValues>({
|
|||||||
<SelectTemplate
|
<SelectTemplate
|
||||||
{...form.register(name, formOptions)}
|
{...form.register(name, formOptions)}
|
||||||
onChange={(newValue: any) => {
|
onChange={(newValue: any) => {
|
||||||
form.setValue(name, newValue);
|
if ('value' in newValue) {
|
||||||
|
form.setValue(name, newValue.value);
|
||||||
|
} else {
|
||||||
|
form.setValue(name, newValue);
|
||||||
|
}
|
||||||
form.trigger(name);
|
form.trigger(name);
|
||||||
}}
|
}}
|
||||||
className={cn('w-full placeholder:text-neutral-600', className)}
|
className={cn('w-full placeholder:text-neutral-600', className)}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"next": "15.1.4",
|
"next": "15.1.4",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-day-picker": "^9.5.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-hot-toast": "^2.5.1",
|
"react-hot-toast": "^2.5.1",
|
||||||
|
|||||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -119,6 +119,7 @@
|
|||||||
"next": "15.1.4",
|
"next": "15.1.4",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-day-picker": "^9.5.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-hot-toast": "^2.5.1",
|
"react-hot-toast": "^2.5.1",
|
||||||
@@ -335,6 +336,11 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@date-fns/tz": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg=="
|
||||||
|
},
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||||
@@ -3316,6 +3322,20 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/date-fns-jalali": {
|
||||||
|
"version": "4.1.0-0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz",
|
||||||
|
"integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
@@ -7896,6 +7916,26 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-day-picker": {
|
||||||
|
"version": "9.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.5.1.tgz",
|
||||||
|
"integrity": "sha512-PxuK8inYLlYgM2zZUVBPsaBM5jI40suPeG+naKyx7kpyF032RRlEAUEjkpW9/poTASh/vyWAOVqjGuGw+47isw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@date-fns/tz": "^1.2.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"date-fns-jalali": "^4.1.0-0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/gpbl"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.0.0",
|
"version": "19.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `selectedForParticipatioon` on the `Participant` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `status` on the `Participant` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `eventAppointmentId` to the `Participant` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterEnum
|
||||||
|
-- This migration adds more than one value to an enum.
|
||||||
|
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||||
|
-- in a single migration. This can be worked around by creating
|
||||||
|
-- multiple migrations, each migration adding only one value to
|
||||||
|
-- the enum.
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TYPE "PERMISSION" ADD VALUE 'PILOT';
|
||||||
|
ALTER TYPE "PERMISSION" ADD VALUE 'DISPO';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Participant" DROP COLUMN "selectedForParticipatioon",
|
||||||
|
DROP COLUMN "status",
|
||||||
|
ADD COLUMN "eventAppointmentId" INTEGER NOT NULL,
|
||||||
|
ADD COLUMN "finisherMoodleCurseCompleted" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
ADD COLUMN "starterMoodleCurseCompleted" BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "EventAppointment" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"eventId" INTEGER NOT NULL,
|
||||||
|
"appointmentDate" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "EventAppointment_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "_EventAppointmentToUser" (
|
||||||
|
"A" INTEGER NOT NULL,
|
||||||
|
"B" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "_EventAppointmentToUser_AB_pkey" PRIMARY KEY ("A","B")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "_EventAppointmentToUser_B_index" ON "_EventAppointmentToUser"("B");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "EventAppointment" ADD CONSTRAINT "EventAppointment_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Participant" ADD CONSTRAINT "Participant_eventAppointmentId_fkey" FOREIGN KEY ("eventAppointmentId") REFERENCES "EventAppointment"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentToUser" ADD CONSTRAINT "_EventAppointmentToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "EventAppointment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentToUser" ADD CONSTRAINT "_EventAppointmentToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Participant" DROP CONSTRAINT "Participant_eventAppointmentId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Participant" ALTER COLUMN "eventAppointmentId" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Participant" ADD CONSTRAINT "Participant_eventAppointmentId_fkey" FOREIGN KEY ("eventAppointmentId") REFERENCES "EventAppointment"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@@ -10,16 +10,30 @@ enum PARTICIPANT_STATUS {
|
|||||||
WAVED
|
WAVED
|
||||||
}
|
}
|
||||||
|
|
||||||
model Participant {
|
model EventAppointment {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
userId String @map(name: "user_id")
|
eventId Int
|
||||||
status PARTICIPANT_STATUS
|
appointmentDate DateTime
|
||||||
selectedForParticipatioon Boolean @default(false)
|
presenterId String
|
||||||
statusLog Json[]
|
|
||||||
eventId Int
|
|
||||||
// relations:
|
// relations:
|
||||||
user User @relation(fields: [userId], references: [id])
|
Users User[] @relation("EventAppointmentUser")
|
||||||
Event Event? @relation(fields: [eventId], references: [id])
|
participants Participant[]
|
||||||
|
Event Event @relation(fields: [eventId], references: [id])
|
||||||
|
Presenter User @relation(fields: [presenterId], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Participant {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
userId String @map(name: "user_id")
|
||||||
|
starterMoodleCurseCompleted Boolean @default(false)
|
||||||
|
finisherMoodleCurseCompleted Boolean @default(false)
|
||||||
|
eventAppointmentId Int?
|
||||||
|
statusLog Json[]
|
||||||
|
eventId Int
|
||||||
|
// relations:
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
Event Event @relation(fields: [eventId], references: [id])
|
||||||
|
EventAppointment EventAppointment? @relation(fields: [eventAppointmentId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Event {
|
model Event {
|
||||||
@@ -38,6 +52,7 @@ model Event {
|
|||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
participants Participant[]
|
participants Participant[]
|
||||||
|
appointments EventAppointment[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model File {
|
model File {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ enum PERMISSION {
|
|||||||
ADMIN_EVENT
|
ADMIN_EVENT
|
||||||
ADMIN_USER
|
ADMIN_USER
|
||||||
SUSPENDED
|
SUSPENDED
|
||||||
|
PILOT
|
||||||
|
DISPO
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
@@ -30,9 +32,11 @@ model User {
|
|||||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||||
|
|
||||||
// relations:
|
// relations:
|
||||||
oauthTokens OAuthToken[]
|
oauthTokens OAuthToken[]
|
||||||
discordAccounts DiscordAccount[]
|
discordAccounts DiscordAccount[]
|
||||||
participants Participant[]
|
participants Participant[]
|
||||||
|
EventAppointmentUser EventAppointment[] @relation("EventAppointmentUser")
|
||||||
|
EventAppointment EventAppointment[]
|
||||||
|
|
||||||
@@map(name: "users")
|
@@map(name: "users")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user