This commit is contained in:
Matthias Hochmeister
2026-03-16 15:26:43 +01:00
parent 023bd7acbb
commit c15d4a50e0
13 changed files with 142 additions and 43 deletions

View File

@@ -70,7 +70,7 @@ import {
import { useNavigate, useSearchParams } from 'react-router-dom';
import DashboardLayout from '../components/dashboard/DashboardLayout';
import ChatAwareFab from '../components/shared/ChatAwareFab';
import { toGermanDate, toGermanDateTime, fromGermanDate, fromGermanDateTime, isValidGermanDate, isValidGermanDateTime } from '../utils/dateInput';
import { toGermanDateTime, fromGermanDate, fromGermanDateTime, isValidGermanDateTime } from '../utils/dateInput';
import { useAuth } from '../contexts/AuthContext';
import { useNotification } from '../contexts/NotificationContext';
import { trainingApi } from '../services/training';
@@ -213,6 +213,24 @@ function fromDatetimeLocal(value: string): string {
return new Date(value).toISOString();
}
/** ISO string → YYYY-MM-DDTHH:MM (for type="datetime-local") */
function toDatetimeLocalValue(iso: string | null | undefined): string {
if (!iso) return '';
const d = new Date(iso);
if (isNaN(d.getTime())) return '';
const pad = (n: number) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
/** ISO string → YYYY-MM-DD (for type="date") */
function toDateInputValue(iso: string | null | undefined): string {
if (!iso) return '';
const d = new Date(iso);
if (isNaN(d.getTime())) return '';
const pad = (n: number) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
// ──────────────────────────────────────────────────────────────────────────────
// Types for unified calendar
// ──────────────────────────────────────────────────────────────────────────────
@@ -910,7 +928,11 @@ function parseCsvRow(line: string, lineNo: number): CsvRow {
if (parts.length < 4) {
return { titel: '', datum_von: '', datum_bis: '', ganztaegig: false, ort: null, beschreibung: null, valid: false, error: `Zeile ${lineNo}: Zu wenige Spalten` };
}
const [titel, rawVon, rawBis, rawGanztaegig, ort, , beschreibung] = parts;
const [titel, rawVon, rawBis, rawGanztaegig, ort, ...rest] = parts;
// Support both 7-column (with Kategorie) and 6-column (without) CSVs:
// 7 cols: Titel;Von;Bis;Ganztaegig;Ort;Kategorie;Beschreibung
// 6 cols: Titel;Von;Bis;Ganztaegig;Ort;Beschreibung
const beschreibung = rest.length >= 2 ? rest[1] : rest[0];
const ganztaegig = rawGanztaegig?.trim().toLowerCase() === 'ja';
const convertDate = (raw: string): string => {
@@ -1383,19 +1405,19 @@ function VeranstaltungFormDialog({
const vonDate = new Date(form.datum_von);
const bisDate = new Date(form.datum_bis);
if (isNaN(vonDate.getTime())) {
notification.showError(`Ungültiges Datum Von (Format: ${form.ganztaegig ? '01.03.2025' : '01.03.2025 18:00'})`);
notification.showError('Ungültiges Datum Von');
return;
}
if (isNaN(bisDate.getTime())) {
notification.showError(`Ungültiges Datum Bis (Format: ${form.ganztaegig ? '01.03.2025' : '01.03.2025 18:00'})`);
notification.showError('Ungültiges Datum Bis');
return;
}
if (bisDate < vonDate) {
notification.showError('Datum Bis muss nach Datum Von liegen');
return;
}
if (wiederholungAktiv && wiederholungBis && !isValidGermanDate(wiederholungBis)) {
notification.showError('Ungültiges Datum für Wiederholung Bis (Format: 01.03.2025)');
if (wiederholungAktiv && wiederholungBis && isNaN(new Date(wiederholungBis).getTime())) {
notification.showError('Ungültiges Datum für Wiederholung Bis');
return;
}
@@ -1406,7 +1428,7 @@ function VeranstaltungFormDialog({
wiederholung: (!editingEvent && wiederholungAktiv && wiederholungBis)
? {
typ: wiederholungTyp,
bis: fromGermanDate(wiederholungBis) || wiederholungBis,
bis: wiederholungBis,
intervall: wiederholungTyp === 'wöchentlich' ? wiederholungIntervall : undefined,
wochentag: (wiederholungTyp === 'monatlich_erster_wochentag' || wiederholungTyp === 'monatlich_letzter_wochentag')
? wiederholungWochentag
@@ -1488,39 +1510,39 @@ function VeranstaltungFormDialog({
/>
<TextField
label="Von"
placeholder={form.ganztaegig ? 'TT.MM.JJJJ' : 'TT.MM.JJJJ HH:MM'}
type={form.ganztaegig ? 'date' : 'datetime-local'}
value={
form.ganztaegig
? toGermanDate(form.datum_von)
: toGermanDateTime(form.datum_von)
? toDateInputValue(form.datum_von)
: toDatetimeLocalValue(form.datum_von)
}
onChange={(e) => {
const raw = e.target.value;
if (!raw) return;
const iso = form.ganztaegig
? fromDatetimeLocal(raw ? `${fromGermanDate(raw)} 00:00` : '')
: fromDatetimeLocal(raw);
? new Date(raw + 'T00:00:00').toISOString()
: new Date(raw).toISOString();
handleChange('datum_von', iso);
}}
helperText={form.ganztaegig ? 'Format: 01.03.2025' : 'Format: 01.03.2025 18:00'}
InputLabelProps={{ shrink: true }}
fullWidth
/>
<TextField
label="Bis"
placeholder={form.ganztaegig ? 'TT.MM.JJJJ' : 'TT.MM.JJJJ HH:MM'}
type={form.ganztaegig ? 'date' : 'datetime-local'}
value={
form.ganztaegig
? toGermanDate(form.datum_bis)
: toGermanDateTime(form.datum_bis)
? toDateInputValue(form.datum_bis)
: toDatetimeLocalValue(form.datum_bis)
}
onChange={(e) => {
const raw = e.target.value;
if (!raw) return;
const iso = form.ganztaegig
? fromDatetimeLocal(raw ? `${fromGermanDate(raw)} 23:59` : '')
: fromDatetimeLocal(raw);
? new Date(raw + 'T23:59:00').toISOString()
: new Date(raw).toISOString();
handleChange('datum_bis', iso);
}}
helperText={form.ganztaegig ? 'Format: 01.03.2025' : 'Format: 01.03.2025 18:00'}
InputLabelProps={{ shrink: true }}
fullWidth
/>
@@ -1649,7 +1671,7 @@ function VeranstaltungFormDialog({
<TextField
label="Wiederholungen bis"
size="small"
placeholder="TT.MM.JJJJ"
type="date"
value={wiederholungBis}
onChange={(e) => setWiederholungBis(e.target.value)}
InputLabelProps={{ shrink: true }}