This commit is contained in:
Nicolas
2025-02-22 17:23:27 +01:00
9 changed files with 199 additions and 47 deletions

View File

@@ -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} />;
}; };

View File

@@ -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>

View File

@@ -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)}

View File

@@ -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
View File

@@ -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",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {

View 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")
} }