feat(buchung): Buchungen kopieren

Duplizieren-Button im Bearbeiten-Dialog und Kopier-Icon je Buchung in
Liste und Wochenansicht. Die Kopie oeffnet den Dialog als neue Buchung
fuer heute mit Projekt, Task, Notiz und Zeit der Vorlage. Inaktive
Projekte oder Tasks fallen auf die Standardauswahl zurueck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-25 13:45:44 +02:00
co-authored by Claude Opus 5.5
parent b6ba7860bf
commit 2b9b7a6ae3
4 changed files with 113 additions and 40 deletions
+19 -3
View File
@@ -53,7 +53,7 @@ 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 } | null>(null);
const [modal, setModal] = useState<{ date: string; entry?: TimeEntry | null; template?: TimeEntry | null } | null>(null);
const [exportOpen, setExportOpen] = useState(false);
const [viewUserId, setViewUserId] = useState<number>(user!.id);
const isLead = atLeast(user, 'lead');
@@ -100,6 +100,12 @@ 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. */
const copy = (entry: TimeEntry) => {
if (readOnly) return;
setModal({ date: today, template: entry });
};
/*
* Im Export stehen nur die eigenen Projekte zur Wahl - auch fuer
* Vorgesetzte. Wer einen Nachweis fuer einen Kunden erstellt, filtert nach
@@ -228,12 +234,13 @@ export const Booking: React.FC = () => {
<EntryList
entries={list}
onEdit={readOnly ? undefined : (entry) => setModal({ date: entry.entry_date, entry })}
onCopy={readOnly ? undefined : copy}
emptyText={`Keine Buchungen ${mode === 'week' ? 'in dieser Woche' : mode === 'month' ? 'in diesem Monat' : 'in diesem Jahr'}.`}
/>
</Card>
)}
{layout === 'calendar' && mode === 'week' && (
<WeekView from={range.from} entries={list} readOnly={readOnly} dailyTarget={(weekly / 5) * 60} onAdd={openNew} onEdit={(entry) => !readOnly && setModal({ date: entry.entry_date, entry })} />
<WeekView from={range.from} entries={list} readOnly={readOnly} dailyTarget={(weekly / 5) * 60} onAdd={openNew} onEdit={(entry) => !readOnly && setModal({ date: entry.entry_date, entry })} onCopy={copy} />
)}
{layout === 'calendar' && mode === 'month' && (
<MonthView
@@ -273,7 +280,16 @@ export const Booking: React.FC = () => {
)}
{modal && projects.data && (
<EntryModal projects={projects.data} date={modal.date} entry={modal.entry} onClose={() => setModal(null)} onSaved={() => { entries.reload(); projects.reload(); }} />
<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}
/>
)}
{exportOpen && (
<ExportDialog