new custom modalbox for event input

This commit is contained in:
2025-07-03 16:06:20 +00:00
parent 7d773739d5
commit f5cb25bdce
4 changed files with 300 additions and 9 deletions

View File

@@ -17,6 +17,7 @@ import { fetchEvents } from './apiEvents';
import { fetchGroups } from './apiGroups';
import { getGroupColor } from './groupColors';
import { deleteEvent } from './apiEvents';
import CustomEventModal from './components/CustomEventModal';
// Typ für Gruppe ergänzen
type Group = {
@@ -94,6 +95,8 @@ const Appointments: React.FC = () => {
const [groups, setGroups] = useState<Group[]>([]);
const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null);
const [events, setEvents] = useState<Event[]>([]);
const [modalOpen, setModalOpen] = useState(false);
const [modalInitialData, setModalInitialData] = useState({});
// Gruppen laden
useEffect(() => {
@@ -129,8 +132,6 @@ const Appointments: React.FC = () => {
}
}, [selectedGroupId]);
console.log('Aktuelle Events:', events);
return (
<div>
<h1 className="text-2xl font-bold mb-4">Terminmanagement</h1>
@@ -153,6 +154,26 @@ const Appointments: React.FC = () => {
style={{ flex: 1 }}
/>
</div>
<button
className="e-btn e-success mb-4"
onClick={() => {
setModalInitialData({});
setModalOpen(true);
}}
>
Neuen Termin anlegen
</button>
<CustomEventModal
open={modalOpen}
onClose={() => setModalOpen(false)}
onSave={eventData => {
console.log('Event-Daten zum Speichern:', eventData);
// Event speichern (API-Aufruf oder State-Update)
setModalOpen(false);
}}
initialData={modalInitialData}
groupName={groups.find(g => g.id === selectedGroupId)?.name ?? ''}
/>
<ScheduleComponent
key={selectedGroupId}
height="750px"
@@ -161,6 +182,16 @@ const Appointments: React.FC = () => {
eventSettings={{
dataSource: events,
}}
cellClick={args => {
// args.startTime und args.endTime sind Date-Objekte
args.cancel = true; // Verhindert die Standardaktion
setModalInitialData({
startDate: args.startTime,
startTime: args.startTime,
endTime: args.endTime,
});
setModalOpen(true);
}}
eventRendered={(args: EventRenderedArgs) => {
if (selectedGroupId && args.data && args.data.Id) {
const groupColor = getGroupColor(selectedGroupId, groups);

View File

@@ -0,0 +1,247 @@
import React from 'react';
import { DialogComponent } from '@syncfusion/ej2-react-popups';
import { TextBoxComponent } from '@syncfusion/ej2-react-inputs';
import { DatePickerComponent, TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import { DropDownListComponent, MultiSelectComponent } from '@syncfusion/ej2-react-dropdowns';
import { CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
type CustomEventData = {
title: string;
startDate: Date | null;
startTime: Date | null;
endTime: Date | null;
type: string;
description: string;
repeat: boolean;
weekdays: number[];
repeatUntil: Date | null;
skipHolidays: boolean;
};
type CustomEventModalProps = {
open: boolean;
onClose: () => void;
onSave: (event: CustomEventData) => void;
initialData?: Partial<CustomEventData>;
groupName?: string; // <--- NEU
};
const weekdayOptions = [
{ value: 0, label: 'Montag' },
{ value: 1, label: 'Dienstag' },
{ value: 2, label: 'Mittwoch' },
{ value: 3, label: 'Donnerstag' },
{ value: 4, label: 'Freitag' },
{ value: 5, label: 'Samstag' },
{ value: 6, label: 'Sonntag' },
];
const typeOptions = [
{ value: 'presentation', label: 'Präsentation' },
{ value: 'website', label: 'Website' },
{ value: 'video', label: 'Video' },
{ value: 'message', label: 'Nachricht' },
{ value: 'webuntis', label: 'WebUntis' },
{ value: 'other', label: 'Sonstiges' },
];
const CustomEventModal: React.FC<CustomEventModalProps> = ({
open,
onClose,
onSave,
initialData = {},
groupName, // <--- NEU
}) => {
const [title, setTitle] = React.useState(initialData.title || '');
const [startDate, setStartDate] = React.useState(initialData.startDate || null);
const [startTime, setStartTime] = React.useState(
initialData.startTime || new Date(0, 0, 0, 9, 0)
);
const [endTime, setEndTime] = React.useState(initialData.endTime || new Date(0, 0, 0, 9, 30));
const [type, setType] = React.useState(initialData.type ?? 'presentation');
const [description, setDescription] = React.useState(initialData.description || '');
const [repeat, setRepeat] = React.useState(initialData.repeat || false);
const [weekdays, setWeekdays] = React.useState<number[]>(initialData.weekdays || []);
const [repeatUntil, setRepeatUntil] = React.useState(initialData.repeatUntil || null);
const [skipHolidays, setSkipHolidays] = React.useState(initialData.skipHolidays || false);
const [errors, setErrors] = React.useState<{ [key: string]: string }>({});
React.useEffect(() => {
if (open && initialData) {
setTitle(initialData.title || '');
setStartDate(initialData.startDate || null);
setStartTime(initialData.startTime || new Date(0, 0, 0, 9, 0));
setEndTime(initialData.endTime || new Date(0, 0, 0, 9, 30));
setType(initialData.type || '');
setDescription(initialData.description || '');
setRepeat(initialData.repeat || false);
setWeekdays(initialData.weekdays || []);
setRepeatUntil(initialData.repeatUntil || null);
setSkipHolidays(initialData.skipHolidays || false);
}
}, [open, initialData]);
const handleSave = () => {
const newErrors: { [key: string]: string } = {};
if (!title.trim()) newErrors.title = 'Titel ist erforderlich';
if (!startDate) newErrors.startDate = 'Startdatum ist erforderlich';
if (!startTime) newErrors.startTime = 'Startzeit ist erforderlich';
if (!endTime) newErrors.endTime = 'Endzeit ist erforderlich';
if (!type) newErrors.type = 'Termintyp ist erforderlich';
// ggf. weitere Felder prüfen
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setErrors({});
onSave({
title,
startDate,
startTime,
endTime,
type,
description,
repeat,
weekdays,
repeatUntil,
skipHolidays,
});
};
return (
<DialogComponent
visible={open}
width="800px"
header={() => (
<div>
Neuen Termin anlegen
{groupName && (
<span style={{ fontWeight: 400, fontSize: 16, marginLeft: 16, color: '#888' }}>
für Raumgruppe: <b>{groupName}</b>
</span>
)}
</div>
)}
showCloseIcon={true}
close={onClose}
isModal={true}
footerTemplate={() => (
<div className="flex gap-2 justify-end">
<button className="e-btn e-danger" onClick={onClose}>
Schließen
</button>
<button className="e-btn e-success" onClick={handleSave}>
Termin(e) speichern
</button>
</div>
)}
>
<div style={{ padding: '24px' }}>
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap' }}>
<div style={{ flex: 1, minWidth: 260 }}>
<div style={{ marginBottom: 12 }}>
<TextBoxComponent
placeholder="Titel"
floatLabelType="Auto"
value={title}
change={e => setTitle(e.value)}
/>
{errors.title && <div style={{ color: 'red', fontSize: 12 }}>{errors.title}</div>}
</div>
<div style={{ marginBottom: 12 }}>
<DatePickerComponent
placeholder="Startdatum"
floatLabelType="Auto"
value={startDate ?? undefined}
change={e => setStartDate(e.value)}
/>
{errors.startDate && (
<div style={{ color: 'red', fontSize: 12 }}>{errors.startDate}</div>
)}
</div>
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
<div style={{ flex: 1 }}>
<TimePickerComponent
placeholder="Startzeit"
floatLabelType="Auto"
value={startTime}
step={30}
change={e => setStartTime(e.value)}
/>
{errors.startTime && (
<div style={{ color: 'red', fontSize: 12 }}>{errors.startTime}</div>
)}
</div>
<div style={{ flex: 1 }}>
<TimePickerComponent
placeholder="Endzeit"
floatLabelType="Auto"
value={endTime}
step={30}
change={e => setEndTime(e.value)}
/>
{errors.endTime && (
<div style={{ color: 'red', fontSize: 12 }}>{errors.endTime}</div>
)}
</div>
</div>
<div style={{ marginBottom: 12 }}>
<DropDownListComponent
dataSource={typeOptions}
fields={{ text: 'label', value: 'value' }}
placeholder="Termintyp"
value={type}
change={e => setType(e.value as string)}
/>
{errors.type && <div style={{ color: 'red', fontSize: 12 }}>{errors.type}</div>}
</div>
</div>
<div style={{ flex: 1, minWidth: 260 }}>
<div style={{ marginBottom: 12 }}>
<CheckBoxComponent
label="Wiederholender Termin"
checked={repeat}
change={e => setRepeat(e.checked)}
/>
</div>
<div style={{ marginBottom: 12 }}>
<MultiSelectComponent
key={repeat ? 'enabled' : 'disabled'}
dataSource={weekdayOptions}
fields={{ text: 'label', value: 'value' }}
placeholder="Wochentage"
value={weekdays}
change={e => setWeekdays(e.value as number[])}
disabled={!repeat}
showDropDownIcon={true}
closePopupOnSelect={false}
/>
</div>
<div style={{ marginBottom: 12 }}>
<DatePickerComponent
key={repeat ? 'enabled' : 'disabled'}
placeholder="Wiederholung bis"
floatLabelType="Auto"
value={repeatUntil ?? undefined}
change={e => setRepeatUntil(e.value)}
disabled={!repeat}
/>
</div>
<div style={{ marginBottom: 12 }}>
<CheckBoxComponent
label="Ferientage berücksichtigen"
checked={skipHolidays}
change={e => setSkipHolidays(e.checked)}
disabled={!repeat}
/>
</div>
</div>
</div>
</div>
</DialogComponent>
);
};
export default CustomEventModal;