new custom modalbox for event input
This commit is contained in:
@@ -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);
|
||||
|
||||
247
dashboard/src/components/CustomEventModal.tsx
Normal file
247
dashboard/src/components/CustomEventModal.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user