added basic data for event list, md editor and viewer

This commit is contained in:
PxlLoewe
2025-02-23 23:02:29 +01:00
parent 11d3e71b7a
commit c5b8a7c4cb
9 changed files with 5797 additions and 100 deletions

View File

@@ -22,6 +22,7 @@ import {
} 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,
@@ -107,12 +108,7 @@ export const Form = ({
<FileText className="w-5 h-5" /> Allgemeines
</h2>
<Input form={form} label="Name" name="name" className="input-sm" />
<Input
form={form}
label="Beschreibung"
name="description"
className="input-sm"
/>
<MarkdownEditor form={form} name="description" />
<Input
form={form}
label="Maximale Teilnehmer (Nur für live Events)"
@@ -174,12 +170,14 @@ export const Form = ({
<h2 className="card-title">
<Calendar className="w-5 h-5" /> Termine
</h2>
<button
className="btn btn-primary btn-outline"
onClick={() => addParticipantModal.current?.showModal()}
>
Hinzufügen
</button>
{event && (
<button
className="btn btn-primary btn-outline"
onClick={() => addParticipantModal.current?.showModal()}
>
Hinzufügen
</button>
)}
</div>
<PaginatedTable
@@ -233,6 +231,7 @@ export const Form = ({
type="button"
onClick={() => {
console.log(row.original);
// TODO: open modal to edit appointment
}}
className="btn btn-sm btn-outline"
>

View File

@@ -1,28 +1,15 @@
"use client";
import { DrawingPinFilledIcon, EnterIcon } from "@radix-ui/react-icons";
import { User } from "@repo/db";
import ModalBtn from "./modalBtn";
'use client';
import { DrawingPinFilledIcon, EnterIcon } from '@radix-ui/react-icons';
import { Event, User } from '@repo/db';
import ModalBtn from './modalBtn';
import MDEditor from '@uiw/react-md-editor';
export const KursItem = ({
user,
title,
type,
beschreibung,
badge,
moodleReq,
}: {
user: User;
title: string;
type: string;
beschreibung: string;
badge: string;
moodleReq: boolean;
}) => {
export const KursItem = ({ user, event }: { user: User; event: Event }) => {
return (
<div className="col-span-full">
<div className="card bg-base-200 shadow-xl mb-4">
<div className="card-body">
<h2 className="card-title">{title}</h2>
<h2 className="card-title">{event.name}</h2>
<div className="absolute top-0 right-0 m-4">
<span className="badge badge-info badge-outline">
Zusatzqualifikation
@@ -30,21 +17,48 @@ export const KursItem = ({
</div>
<div className="grid grid-cols-6 gap-4">
<div className="col-span-4">
<p className="text-left text-balance">{beschreibung}</p>
<div className="text-left text-balance">
<MDEditor.Markdown
source={event.description}
className="whitespace-pre-wrap"
style={{
backgroundColor: 'transparent',
}}
/>
</div>
</div>
<div className="col-span-2">{badge}</div>
<div className="col-span-2">{event.finishedBadges}</div>
</div>
<div className="card-actions flex justify-between items-center mt-5">
<p className="text-gray-600 text-left flex items-center gap-2">
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen:</b>
<a className="link link-info" href="">
Moodle Kurs /MOODLEKURSTITLE\
</a>
</p>
<div>
<p className="text-gray-600 text-left flex items-center gap-2">
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen: </b>
{(!event.starterMoodleCourseId ||
!event.requiredBadges.length) &&
'Keine'}
{event.starterMoodleCourseId && (
<a className="link link-info" href="">
Moodle Kurs {event.starterMoodleCourseId}
</a>
)}
</p>
{!!event.requiredBadges.length && (
<div className="flex ml-6">
<b className="text-gray-600 text-left">Abzeichen:</b>
<div className="flex gap-2">
{event.requiredBadges.map((badge) => (
<div className="badge badge-secondary badge-outline">
{badge}
</div>
))}
</div>
</div>
)}
</div>
<ModalBtn
title={title}
dates={["Dienstag, 25 Februar 2025", "Mittwoch, 26 Februar 2025"]}
modalId={title + "_modal" + Math.random()}
title={event.name}
dates={['Dienstag, 25 Februar 2025', 'Mittwoch, 26 Februar 2025']}
modalId={`${event.name}_modal.${event.id}`}
/>
</div>
</div>

View File

@@ -1,22 +1,16 @@
import { getServerSession } from "../../api/auth/[...nextauth]/auth";
import { PrismaClient } from "@repo/db";
import { PilotKurs, KursItem } from "./_components/item";
import {
RocketIcon,
DrawingPinFilledIcon,
EnterIcon,
} from "@radix-ui/react-icons";
import { getServerSession } from '../../api/auth/[...nextauth]/auth';
import { PrismaClient } from '@repo/db';
import { PilotKurs, KursItem } from './_components/item';
import { RocketIcon } from '@radix-ui/react-icons';
export default async () => {
const prisma = new PrismaClient();
const session = await getServerSession();
if (!session) return null;
const user = await prisma.user.findFirst({
where: {
id: session.user.id,
},
});
const user = session.user;
const events = await prisma.event.findMany();
if (!user) return null;
return (
<div className="grid grid-cols-6 gap-4">
<div className="col-span-full">
@@ -25,21 +19,9 @@ export default async () => {
</p>
</div>
<PilotKurs user={user} />
<KursItem
user={user}
title="Einsteigerkurs für Disponenten"
type="1"
beschreibung="In diesem Kurs lernen Teilnehmer die Aufgaben eines
Disponenten in der Rettungsfliegerei kennen. Dazu gehören die
Koordination von Notfalleinsätzen, die effiziente Planung von
Ressourcen und die Kommunikation mit Piloten sowie
Rettungsdiensten. Der Kurs vermittelt praxisnahe Kenntnisse
für die schnelle und präzise Entscheidungsfindung unter
Zeitdruck, um eine reibungslose Abwicklung von
Rettungseinsätzen zu gewährleisten."
badge="Badge"
moodleReq={true}
/>
{events.map((event) => (
<KursItem user={user} event={event} key={event.id} />
))}
</div>
);
};

View File

@@ -90,9 +90,9 @@ export function PaginatedTable<TData>({
return (
<div className="space-y-4 m-4">
{searchFields.length > 0 && (
<div className="flex items-center gap-2">
<div className="flex-1">{leftOfSearch}</div>
<div className="flex items-center gap-2">
<div className="flex-1">{leftOfSearch}</div>
{searchFields.length > 0 && (
<input
type="text"
placeholder="Suchen..."
@@ -103,9 +103,9 @@ export function PaginatedTable<TData>({
}}
className="input input-bordered w-full max-w-xs justify-end"
/>
<div className="flex justify-center">{rightOfSearch}</div>
</div>
)}
)}
<div className="flex justify-center">{rightOfSearch}</div>
</div>
<SortableTable
data={data}
prismaModel={prismaModel}

View File

@@ -0,0 +1,46 @@
'use client';
import MDEditor from '@uiw/react-md-editor';
import {
FieldValues,
Path,
RegisterOptions,
UseFormReturn,
} from 'react-hook-form';
import { cn } from '../../../helper/cn';
interface MarkdownEditorProps<T extends FieldValues> {
name: Path<T>;
form: UseFormReturn<T>;
formOptions?: RegisterOptions<T>;
label?: string;
placeholder?: string;
className?: string;
}
export const MarkdownEditor = <T extends FieldValues>({
name,
label = name,
placeholder = label,
form,
className,
}: MarkdownEditorProps<T>) => {
return (
<label className="floating-label w-full mt-5">
<span className="text-lg flex items-center gap-2">{label}</span>
<div className={cn('border rounded-lg p-2 w-full', className)}>
<MDEditor
value={form.watch(name)}
onChange={(value) =>
form.setValue(name, value as any, { shouldValidate: true })
}
textareaProps={{ placeholder }}
/>
</div>
{form.formState.errors[name] && (
<p className="text-error">
{form.formState.errors[name].message as string}
</p>
)}
</label>
);
};

View File

@@ -23,9 +23,11 @@ export const Switch = <T extends FieldValues>({
...inputProps
}: InputProps<T>) => {
return (
<div className="form-control">
<label className="label cursor-pointer">
<span className={cn('label-text', className)}>{label}</span>
<div className="form-control ">
<label className="label cursor-pointer w-full">
<span className={cn('label-text text-left w-full', className)}>
{label}
</span>
<input type="checkbox" className="toggle" {...form.register(name)} />
</label>
</div>

View File

@@ -1,4 +1,5 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const removeImports = require('next-remove-imports')();
const nextConfig = removeImports({});
export default nextConfig;

View File

@@ -14,14 +14,18 @@
"@repo/db": "*",
"@repo/ui": "*",
"@tanstack/react-table": "^8.20.6",
"@uiw/react-md-editor": "^4.0.5",
"axios": "^1.7.9",
"bcryptjs": "^2.4.3",
"clsx": "^2.1.1",
"i": "^0.3.7",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"lucide-react": "^0.474.0",
"next": "15.1.4",
"next-auth": "^4.24.11",
"next-remove-imports": "^1.0.12",
"npm": "^11.1.0",
"react": "^19.0.0",
"react-day-picker": "^9.5.1",
"react-dom": "^19.0.0",

5687
package-lock.json generated

File diff suppressed because it is too large Load Diff