feat(buchung): Buchung per Monatskalender auf mehrere Tage kopieren

Das Kopier-Icon (Liste, Wochenansicht) und "Kopieren" im Bearbeiten-Dialog
oeffnen jetzt einen kleinen Monatskalender. Dort werden beliebige Tage
an- und abgewaehlt, auch ueber Monate hinweg, und in einem Schritt gebucht.

- Tage ausserhalb der Projektlaufzeit bzw. ohne Budgettopf sind gesperrt.
- Urlaub/Feiertage sind markiert, bereits gebuchte Stunden je Tag sichtbar.
- "Werktage" waehlt Mo-Fr des Monats ohne Urlaub und Feiertag.
- Buchungen laufen nacheinander; fehlgeschlagene Tage bleiben ausgewaehlt.
- Der Vorlage-Modus im Buchungsdialog entfaellt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-25 13:51:18 +02:00
co-authored by Claude Opus 5.5
parent 2b9b7a6ae3
commit ef709d423c
5 changed files with 304 additions and 25 deletions
+14 -5
View File
@@ -3,6 +3,7 @@ import { Avatar, Button, Card, ErrorBox, Segmented, Spinner, inputClass } from '
import { Icon } from '../ui/Icon';
import { Donut } from '../ui/charts';
import { EntryModal } from './EntryModal';
import { CopyDialog } from './CopyDialog';
import { ExportDialog } from './ExportDialog';
import { WeekView } from './WeekView';
import { MonthView } from './MonthView';
@@ -53,7 +54,8 @@ export const Booking: React.FC = () => {
const [mode, setMode] = usePersistentState<BookingMode>('pulse_booking_mode', 'week');
const [layout, setLayout] = usePersistentState<BookingLayout>('pulse_booking_layout', 'calendar');
const [anchor, setAnchor] = useState(todayIso());
const [modal, setModal] = useState<{ date: string; entry?: TimeEntry | null; template?: TimeEntry | null } | null>(null);
const [modal, setModal] = useState<{ date: string; entry?: TimeEntry | null } | null>(null);
const [copyEntry, setCopyEntry] = useState<TimeEntry | null>(null);
const [exportOpen, setExportOpen] = useState(false);
const [viewUserId, setViewUserId] = useState<number>(user!.id);
const isLead = atLeast(user, 'lead');
@@ -100,10 +102,11 @@ export const Booking: React.FC = () => {
setModal({ date: date ?? (today >= range.from && today <= range.to ? today : range.from) });
};
/** Kopie einer Buchung als neue Buchung fuer heute; Datum ist im Dialog aenderbar. */
/** Kopieren: Tage im Monatskalender waehlen, auf die die Buchung kopiert wird. */
const copy = (entry: TimeEntry) => {
if (readOnly) return;
setModal({ date: today, template: entry });
setModal(null);
setCopyEntry(entry);
};
/*
@@ -281,16 +284,22 @@ export const Booking: React.FC = () => {
{modal && projects.data && (
<EntryModal
key={modal.entry?.id ?? `new-${modal.template?.id ?? ''}`}
projects={projects.data}
date={modal.date}
entry={modal.entry}
template={modal.template}
onClose={() => setModal(null)}
onSaved={() => { entries.reload(); projects.reload(); }}
onDuplicate={copy}
/>
)}
{copyEntry && projects.data && (
<CopyDialog
entry={copyEntry}
projects={projects.data}
onClose={() => setCopyEntry(null)}
onSaved={() => { entries.reload(); projects.reload(); }}
/>
)}
{exportOpen && (
<ExportDialog
month={anchor.slice(0, 7)}