feat: Implement Event Deep-Linking (?event=ID) and TEILEN share link buttons
This commit is contained in:
Vendored
+2
-2
@@ -11,8 +11,8 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||||
<title>Schichtplaner — Veranstaltungsschichtpläne</title>
|
<title>Schichtplaner — Veranstaltungsschichtpläne</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Mp9FLWF3.js"></script>
|
<script type="module" crossorigin src="/assets/index-BUskNRc4.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-B6pC8vGA.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DHeBvKbG.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-paper text-main font-sans antialiased min-h-screen">
|
<body class="bg-paper text-main font-sans antialiased min-h-screen">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
+53
-1
@@ -180,16 +180,38 @@ export default function App() {
|
|||||||
} catch (e) {}
|
} 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 () => {
|
const refreshEvents = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await apiFetch('/events/');
|
const data = await apiFetch('/events/');
|
||||||
const list = data.results || data;
|
const list = data.results || data;
|
||||||
setEvents(list);
|
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);
|
setSelectedEventId(list[0].id);
|
||||||
fetchEventMatrix(list[0].id);
|
fetchEventMatrix(list[0].id);
|
||||||
|
updateUrlParam(list[0].id);
|
||||||
} else if (selectedEventId) {
|
} else if (selectedEventId) {
|
||||||
fetchEventMatrix(selectedEventId);
|
fetchEventMatrix(selectedEventId);
|
||||||
|
updateUrlParam(selectedEventId);
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
};
|
};
|
||||||
@@ -205,6 +227,34 @@ export default function App() {
|
|||||||
setSelectedEventId(id);
|
setSelectedEventId(id);
|
||||||
fetchEventMatrix(id);
|
fetchEventMatrix(id);
|
||||||
setActiveTab('schedule');
|
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 = () => {
|
const handleLogout = () => {
|
||||||
@@ -464,6 +514,7 @@ export default function App() {
|
|||||||
alert(err.message || 'Aktion fehlgeschlagen.');
|
alert(err.message || 'Aktion fehlgeschlagen.');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onShareEvent={handleShareEvent}
|
||||||
onEditEvent={(evt) => {
|
onEditEvent={(evt) => {
|
||||||
setEventToEdit(evt);
|
setEventToEdit(evt);
|
||||||
setActiveTab('event-editor');
|
setActiveTab('event-editor');
|
||||||
@@ -572,6 +623,7 @@ export default function App() {
|
|||||||
onGenerateClaimLink={handleGenerateClaimLink}
|
onGenerateClaimLink={handleGenerateClaimLink}
|
||||||
onExportPdf={handleExportPdf}
|
onExportPdf={handleExportPdf}
|
||||||
onPrintView={handlePrintView}
|
onPrintView={handlePrintView}
|
||||||
|
onShareLink={() => handleShareEvent(selectedEventId)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */
|
/* 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 */
|
/* Hallmark · component: shift-matrix-table · genre: editorial · theme: Atelier (Dark & Light) · studied-DNA: https://inihaus.de */
|
||||||
import React from 'react';
|
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({
|
export default function ShiftMatrixTable({
|
||||||
event,
|
event,
|
||||||
@@ -12,6 +12,7 @@ export default function ShiftMatrixTable({
|
|||||||
onGenerateClaimLink,
|
onGenerateClaimLink,
|
||||||
onExportPdf,
|
onExportPdf,
|
||||||
onPrintView,
|
onPrintView,
|
||||||
|
onShareLink,
|
||||||
onEditEvent,
|
onEditEvent,
|
||||||
onDeleteEvent
|
onDeleteEvent
|
||||||
}) {
|
}) {
|
||||||
@@ -92,6 +93,15 @@ export default function ShiftMatrixTable({
|
|||||||
<Trash2 className="w-3.5 h-3.5" /> LÖSCHEN
|
<Trash2 className="w-3.5 h-3.5" /> LÖSCHEN
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{onShareLink && (
|
||||||
|
<button
|
||||||
|
onClick={onShareLink}
|
||||||
|
className="px-3.5 py-1.5 rounded-sm bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/30 transition flex items-center gap-1.5 font-bold"
|
||||||
|
title="Direktlink zu diesem Schichtplan kopieren"
|
||||||
|
>
|
||||||
|
<Share2 className="w-3.5 h-3.5" /> TEILEN
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={onPrintView}
|
onClick={onPrintView}
|
||||||
className="px-3.5 py-1.5 rounded-sm bg-surface hover:bg-surface-hover text-main border border-grid transition flex items-center gap-1.5 font-bold"
|
className="px-3.5 py-1.5 rounded-sm bg-surface hover:bg-surface-hover text-main border border-grid transition flex items-center gap-1.5 font-bold"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */
|
/* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */
|
||||||
/* Hallmark · redesign: HomePage · studied-DNA: https://inihaus.de · macrostructure: Newsstand Feed */
|
/* Hallmark · redesign: HomePage · studied-DNA: https://inihaus.de · macrostructure: Newsstand Feed */
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Search, Calendar, MapPin, Clock, ArrowRight, Plus, EyeOff, CheckCircle2, Edit3, ShieldAlert, Trash2 } from 'lucide-react';
|
import { Search, Calendar, MapPin, Clock, ArrowRight, Plus, EyeOff, CheckCircle2, Edit3, ShieldAlert, Trash2, Share2 } from 'lucide-react';
|
||||||
|
|
||||||
export default function HomePage({
|
export default function HomePage({
|
||||||
events,
|
events,
|
||||||
@@ -9,6 +9,7 @@ export default function HomePage({
|
|||||||
onSelectEvent,
|
onSelectEvent,
|
||||||
onOpenCreateEvent,
|
onOpenCreateEvent,
|
||||||
onToggleEventActive,
|
onToggleEventActive,
|
||||||
|
onShareEvent,
|
||||||
onEditEvent,
|
onEditEvent,
|
||||||
onDeleteEvent,
|
onDeleteEvent,
|
||||||
branding
|
branding
|
||||||
@@ -190,6 +191,16 @@ export default function HomePage({
|
|||||||
Schichtplan <ArrowRight className="w-3.5 h-3.5" />
|
Schichtplan <ArrowRight className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{onShareEvent && (
|
||||||
|
<button
|
||||||
|
onClick={() => onShareEvent(evt)}
|
||||||
|
className="py-1.5 px-2.5 rounded-sm text-[10px] font-mono font-bold bg-blue-500/10 text-blue-400 border border-blue-500/20 hover:bg-blue-500/20 transition flex items-center gap-1 shrink-0"
|
||||||
|
title="Direktlink zu dieser Veranstaltung kopieren"
|
||||||
|
>
|
||||||
|
<Share2 className="w-3.5 h-3.5" /> TEILEN
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{canManage && onEditEvent && (
|
{canManage && onEditEvent && (
|
||||||
<button
|
<button
|
||||||
onClick={() => onEditEvent(evt)}
|
onClick={() => onEditEvent(evt)}
|
||||||
|
|||||||
Reference in New Issue
Block a user