diff --git a/frontend/dist/index.html b/frontend/dist/index.html index a5475d6..8ab2616 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -11,8 +11,8 @@ Schichtplaner — Veranstaltungsschichtpläne - - + +
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0d47f67..b40ce55 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -180,16 +180,38 @@ export default function App() { } catch (e) {} }; + const updateUrlParam = (id) => { + if (!id) return; + try { + const url = new URL(window.location.href); + url.searchParams.set('event', id); + window.history.replaceState(null, '', url.pathname + url.search); + } catch (e) {} + }; + const refreshEvents = async () => { try { const data = await apiFetch('/events/'); const list = data.results || data; setEvents(list); - if (list.length > 0 && !selectedEventId) { + + // Check URL query parameters for deep linking (?event=12) + const urlParams = new URLSearchParams(window.location.search); + const urlEventParam = urlParams.get('event') || urlParams.get('event_id'); + const targetIdFromUrl = urlEventParam ? parseInt(urlEventParam, 10) : null; + + if (targetIdFromUrl && list.some(e => e.id === targetIdFromUrl)) { + setSelectedEventId(targetIdFromUrl); + fetchEventMatrix(targetIdFromUrl); + setActiveTab('schedule'); + updateUrlParam(targetIdFromUrl); + } else if (list.length > 0 && !selectedEventId) { setSelectedEventId(list[0].id); fetchEventMatrix(list[0].id); + updateUrlParam(list[0].id); } else if (selectedEventId) { fetchEventMatrix(selectedEventId); + updateUrlParam(selectedEventId); } } catch (e) {} }; @@ -205,6 +227,34 @@ export default function App() { setSelectedEventId(id); fetchEventMatrix(id); setActiveTab('schedule'); + updateUrlParam(id); + }; + + const fallbackCopyText = (text) => { + const el = document.createElement('textarea'); + el.value = text; + document.body.appendChild(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); + showNotification('Direktlink zum Schichtplan in Zwischenablage kopiert! 🔗'); + }; + + const handleShareEvent = (targetEventOrId) => { + const targetId = typeof targetEventOrId === 'object' ? targetEventOrId.id : (targetEventOrId || selectedEventId); + if (!targetId) return; + + const shareUrl = `${window.location.origin}${window.location.pathname}?event=${targetId}`; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(shareUrl).then(() => { + showNotification('Direktlink zum Schichtplan in Zwischenablage kopiert! 🔗'); + }).catch(() => { + fallbackCopyText(shareUrl); + }); + } else { + fallbackCopyText(shareUrl); + } }; const handleLogout = () => { @@ -464,6 +514,7 @@ export default function App() { alert(err.message || 'Aktion fehlgeschlagen.'); } }} + onShareEvent={handleShareEvent} onEditEvent={(evt) => { setEventToEdit(evt); setActiveTab('event-editor'); @@ -572,6 +623,7 @@ export default function App() { onGenerateClaimLink={handleGenerateClaimLink} onExportPdf={handleExportPdf} onPrintView={handlePrintView} + onShareLink={() => handleShareEvent(selectedEventId)} /> )} diff --git a/frontend/src/components/ShiftMatrixTable.jsx b/frontend/src/components/ShiftMatrixTable.jsx index 1a8a199..2229de3 100644 --- a/frontend/src/components/ShiftMatrixTable.jsx +++ b/frontend/src/components/ShiftMatrixTable.jsx @@ -1,7 +1,7 @@ /* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */ /* Hallmark · component: shift-matrix-table · genre: editorial · theme: Atelier (Dark & Light) · studied-DNA: https://inihaus.de */ import React from 'react'; -import { Clock, UserCheck, Shield, Printer, Download, Award, CheckCircle2, UserX, UserPlus, MapPin, Edit3, Trash2 } from 'lucide-react'; +import { Clock, UserCheck, Shield, Printer, Download, Award, CheckCircle2, UserX, UserPlus, MapPin, Edit3, Trash2, Share2 } from 'lucide-react'; export default function ShiftMatrixTable({ event, @@ -12,6 +12,7 @@ export default function ShiftMatrixTable({ onGenerateClaimLink, onExportPdf, onPrintView, + onShareLink, onEditEvent, onDeleteEvent }) { @@ -92,6 +93,15 @@ export default function ShiftMatrixTable({ LÖSCHEN )} + {onShareLink && ( + + )} + {onShareEvent && ( + + )} + {canManage && onEditEvent && (