Added appointment table
This commit is contained in:
@@ -16,7 +16,11 @@ export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
publicId: true,
|
publicId: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(users);
|
const appointments = await prisma.eventAppointment.findMany({
|
||||||
|
where: {
|
||||||
|
eventId: parseInt(id),
|
||||||
|
},
|
||||||
|
});
|
||||||
if (!event) return <div>Event not found</div>;
|
if (!event) return <div>Event not found</div>;
|
||||||
return <Form event={event} users={users} />;
|
return <Form event={event} users={users} appointments={appointments} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import {
|
import {
|
||||||
|
EventAppointmentOptionalDefaults,
|
||||||
EventAppointmentOptionalDefaultsSchema,
|
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, User } from '@repo/db';
|
import { BADGES, Event, EventAppointment, User } from '@repo/db';
|
||||||
import { Bot, Calendar, FileText, UserIcon } from 'lucide-react';
|
import { Bot, Calendar, 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';
|
||||||
import { deleteEvent, upsertEvent } from '../action';
|
import { deleteEvent, upsertAppointment, upsertEvent } from '../action';
|
||||||
import { Button } from '../../../../_components/ui/Button';
|
import { Button } from '../../../../_components/ui/Button';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect, useRouter } from 'next/navigation';
|
||||||
import { Switch } from '../../../../_components/ui/Switch';
|
import { Switch } from '../../../../_components/ui/Switch';
|
||||||
import { PaginatedTable } from '../../../../_components/PaginatedTable';
|
import {
|
||||||
|
PaginatedTable,
|
||||||
|
PaginatedTableRef,
|
||||||
|
} from '../../../../_components/PaginatedTable';
|
||||||
import { Select } from '../../../../_components/ui/Select';
|
import { Select } from '../../../../_components/ui/Select';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
|
|
||||||
export const Form = ({
|
export const Form = ({
|
||||||
event,
|
event,
|
||||||
users,
|
users,
|
||||||
|
appointments = [],
|
||||||
}: {
|
}: {
|
||||||
event?: Event;
|
event?: Event;
|
||||||
users: {
|
users: {
|
||||||
@@ -29,15 +35,21 @@ export const Form = ({
|
|||||||
lastname: string;
|
lastname: string;
|
||||||
publicId: string;
|
publicId: string;
|
||||||
}[];
|
}[];
|
||||||
|
appointments?: EventAppointment[];
|
||||||
}) => {
|
}) => {
|
||||||
|
const { data: session } = useSession();
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||||
defaultValues: event,
|
defaultValues: event,
|
||||||
});
|
});
|
||||||
const appointmentForm = useForm({
|
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
||||||
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||||
|
defaultValues: {
|
||||||
|
eventId: event?.id,
|
||||||
|
presenterId: session?.user?.id,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
console.log(appointmentForm.formState.errors);
|
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
||||||
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);
|
||||||
@@ -51,20 +63,26 @@ export const Form = ({
|
|||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<h3 className="font-bold text-lg">Teilnehmer hinzufügen</h3>
|
<h3 className="font-bold text-lg">Termin hinzufügen</h3>
|
||||||
<form
|
<form
|
||||||
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||||
console.log(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"
|
||||||
>
|
>
|
||||||
<Select
|
<Input
|
||||||
form={appointmentForm}
|
form={appointmentForm}
|
||||||
name="userId"
|
label="Datum"
|
||||||
label="Teilnehmer"
|
name="appointmentDate"
|
||||||
options={users.map((user) => ({
|
type="date"
|
||||||
label: `${user.firstname} ${user.lastname} (${user.publicId})`,
|
|
||||||
value: user.id,
|
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<Button type="submit" className="btn btn-primary">
|
<Button type="submit" className="btn btn-primary">
|
||||||
@@ -165,16 +183,66 @@ export const Form = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
|
ref={appointmentsTableRef}
|
||||||
prismaModel={'eventAppointment'}
|
prismaModel={'eventAppointment'}
|
||||||
filter={{
|
filter={{
|
||||||
eventId: event?.id,
|
eventId: event?.id,
|
||||||
}}
|
}}
|
||||||
include={[
|
include={{
|
||||||
|
Presenter: true,
|
||||||
|
Participants: true,
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
{
|
{
|
||||||
user: true,
|
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);
|
||||||
|
}}
|
||||||
|
className="btn btn-sm btn-outline"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
columns={[]}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { prisma, Prisma, Event, Participant } from '@repo/db';
|
import { prisma, Prisma, Event, Participant, EventAppointment } from '@repo/db';
|
||||||
|
|
||||||
export const upsertEvent = async (
|
export const upsertEvent = async (
|
||||||
event: Prisma.EventCreateInput,
|
event: Prisma.EventCreateInput,
|
||||||
@@ -19,6 +19,26 @@ export const deleteEvent = async (id: Event['id']) => {
|
|||||||
await prisma.event.delete({ where: { id: id } });
|
await prisma.event.delete({ where: { id: id } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const upsertAppointment = async (
|
||||||
|
eventAppointment: Prisma.XOR<
|
||||||
|
Prisma.EventAppointmentCreateInput,
|
||||||
|
Prisma.EventAppointmentUncheckedCreateInput
|
||||||
|
>,
|
||||||
|
id?: EventAppointment['id']
|
||||||
|
) => {
|
||||||
|
const newEventAppointment = id
|
||||||
|
? await prisma.eventAppointment.update({
|
||||||
|
where: { id: id },
|
||||||
|
data: eventAppointment,
|
||||||
|
})
|
||||||
|
: await prisma.eventAppointment.create({ data: eventAppointment });
|
||||||
|
return newEventAppointment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteAppoinement = async (id: Event['id']) => {
|
||||||
|
await prisma.eventAppointment.delete({ where: { id: id } });
|
||||||
|
};
|
||||||
|
|
||||||
export const upsertParticipant = async (
|
export const upsertParticipant = async (
|
||||||
participant: Prisma.ParticipantCreateInput,
|
participant: Prisma.ParticipantCreateInput,
|
||||||
id?: Participant['id']
|
id?: Participant['id']
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { useEffect, useState, useCallback } from 'react';
|
import {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useCallback,
|
||||||
|
Ref,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from 'react';
|
||||||
import SortableTable, { Pagination, SortableTableProps } from './Table';
|
import SortableTable, { Pagination, SortableTableProps } from './Table';
|
||||||
import { PrismaClient } from '@repo/db';
|
import { PrismaClient } from '@repo/db';
|
||||||
import { getData } from './pagiantedTableActions';
|
import { getData } from './pagiantedTableActions';
|
||||||
|
|
||||||
|
export interface PaginatedTableRef {
|
||||||
|
refresh: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
interface PaginatedTableProps<TData>
|
interface PaginatedTableProps<TData>
|
||||||
extends Omit<SortableTableProps<TData>, 'data'> {
|
extends Omit<SortableTableProps<TData>, 'data'> {
|
||||||
prismaModel: keyof PrismaClient;
|
prismaModel: keyof PrismaClient;
|
||||||
@@ -11,7 +21,8 @@ interface PaginatedTableProps<TData>
|
|||||||
rowsPerPage?: number;
|
rowsPerPage?: number;
|
||||||
showEditButton?: boolean;
|
showEditButton?: boolean;
|
||||||
searchFields?: string[];
|
searchFields?: string[];
|
||||||
include?: Record<string, boolean>[];
|
include?: Record<string, boolean>;
|
||||||
|
ref?: Ref<PaginatedTableRef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PaginatedTable<TData>({
|
export function PaginatedTable<TData>({
|
||||||
@@ -21,6 +32,7 @@ export function PaginatedTable<TData>({
|
|||||||
searchFields = [],
|
searchFields = [],
|
||||||
filter,
|
filter,
|
||||||
include,
|
include,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
}: PaginatedTableProps<TData>) {
|
}: PaginatedTableProps<TData>) {
|
||||||
const [data, setData] = useState<TData[]>([]);
|
const [data, setData] = useState<TData[]>([]);
|
||||||
@@ -29,6 +41,30 @@ export function PaginatedTable<TData>({
|
|||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
|
||||||
|
|
||||||
|
const RefreshTableData = async () => {
|
||||||
|
getData(
|
||||||
|
prismaModel,
|
||||||
|
rowsPerPage,
|
||||||
|
page * rowsPerPage,
|
||||||
|
debouncedSearchTerm,
|
||||||
|
searchFields,
|
||||||
|
filter,
|
||||||
|
include
|
||||||
|
).then((result) => {
|
||||||
|
if (result) {
|
||||||
|
setData(result.data);
|
||||||
|
setTotal(result.total);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
refresh: () => {
|
||||||
|
console.log('refresh');
|
||||||
|
RefreshTableData();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const debounce = (func: Function, delay: number) => {
|
const debounce = (func: Function, delay: number) => {
|
||||||
let timer: NodeJS.Timeout;
|
let timer: NodeJS.Timeout;
|
||||||
return (...args: any[]) => {
|
return (...args: any[]) => {
|
||||||
@@ -45,19 +81,7 @@ export function PaginatedTable<TData>({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData(
|
RefreshTableData();
|
||||||
prismaModel,
|
|
||||||
rowsPerPage,
|
|
||||||
page * rowsPerPage,
|
|
||||||
debouncedSearchTerm,
|
|
||||||
searchFields,
|
|
||||||
filter
|
|
||||||
).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
setData(result.data);
|
|
||||||
setTotal(result.total);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [page, debouncedSearchTerm]);
|
}, [page, debouncedSearchTerm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export async function getData(
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
searchFields: string[],
|
searchFields: string[],
|
||||||
filter?: Record<string, any>,
|
filter?: Record<string, any>,
|
||||||
include?: Record<string, boolean>[]
|
include?: Record<string, boolean>
|
||||||
) {
|
) {
|
||||||
if (!model || !prisma[model]) {
|
if (!model || !prisma[model]) {
|
||||||
return { data: [], total: 0 };
|
return { data: [], total: 0 };
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `_EventAppointmentToUser` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- Added the required column `presenterId` to the `EventAppointment` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentToUser" DROP CONSTRAINT "_EventAppointmentToUser_A_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentToUser" DROP CONSTRAINT "_EventAppointmentToUser_B_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "EventAppointment" ADD COLUMN "presenterId" TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "_EventAppointmentToUser";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "_EventAppointmentUser" (
|
||||||
|
"A" INTEGER NOT NULL,
|
||||||
|
"B" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "_EventAppointmentUser_AB_pkey" PRIMARY KEY ("A","B")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "_EventAppointmentUser_B_index" ON "_EventAppointmentUser"("B");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "EventAppointment" ADD CONSTRAINT "EventAppointment_presenterId_fkey" FOREIGN KEY ("presenterId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentUser" ADD CONSTRAINT "_EventAppointmentUser_A_fkey" FOREIGN KEY ("A") REFERENCES "EventAppointment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_EventAppointmentUser" ADD CONSTRAINT "_EventAppointmentUser_B_fkey" FOREIGN KEY ("B") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -17,7 +17,7 @@ model EventAppointment {
|
|||||||
presenterId String
|
presenterId String
|
||||||
// relations:
|
// relations:
|
||||||
Users User[] @relation("EventAppointmentUser")
|
Users User[] @relation("EventAppointmentUser")
|
||||||
participants Participant[]
|
Participants Participant[]
|
||||||
Event Event @relation(fields: [eventId], references: [id])
|
Event Event @relation(fields: [eventId], references: [id])
|
||||||
Presenter User @relation(fields: [presenterId], references: [id])
|
Presenter User @relation(fields: [presenterId], references: [id])
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user