feat(termin): vollwertige Detailseite + QR/Detail-Link im Kalender
/termin/<slug> ist jetzt echte Seite statt Redirect: - alle Infos ungekürzt (Datum/Zeit in Europe/Berlin, Ort+Maps, Tags, Bild+Lightbox, volle Beschreibung, Anhang, externer Link) - alle Aktionen: ICS, Google, Link kopieren, Teilen, QR, Social-Bild - Link zum Kalender drunter (kein Auto-Redirect mehr) - Breite via Layout-.container-custom (gleich wie andere Seiten) - SEO: OG/Twitter/canonical zentral aus Layout (kein Doppel-Tag), Event-JSON-LD + Breadcrumb für Rich Results / Share-Preview CalendarBlock: QR-Button + "Details →"-Link je Termin (→ /termin/<slug>/). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
import Icon from "@iconify/svelte";
|
||||
import ImageModal from "$lib/components/ImageModal.svelte";
|
||||
import SocialImageModal from "$lib/components/SocialImageModal.svelte";
|
||||
import QrModal from "$lib/components/QrModal.svelte";
|
||||
|
||||
type EventCardItem = CalendarItemData & {
|
||||
start: Date;
|
||||
@@ -458,6 +459,18 @@
|
||||
socialModal = null;
|
||||
}
|
||||
|
||||
// QR-Code zum direkten Ausgeben/Scannen (Aushang, Infostand). Nutzt die
|
||||
// canonical /termin/<slug>-URL (hat OG-Tags + Redirect), Fallback = Hash-
|
||||
// Anchor im Kalender wenn kein Slug.
|
||||
let qrModal = $state<{ value: string; label: string } | null>(null);
|
||||
|
||||
function openQr(item: EventCardItem) {
|
||||
const url = item._slug
|
||||
? `${window.location.origin}/termin/${encodeURIComponent(item._slug)}/`
|
||||
: `${window.location.origin}${window.location.pathname}#${eventId(item)}`;
|
||||
qrModal = { value: url, label: item.title || "Termin" };
|
||||
}
|
||||
|
||||
function eventId(item: EventCardItem): string {
|
||||
if (item._slug) return `event-${item._slug}`;
|
||||
const titlePart = (item.title || "event")
|
||||
@@ -781,8 +794,21 @@
|
||||
|
||||
<!-- Aktionen: Primary-Reihe (Info/Karte/Anhang) + kompakte Action-Toolbar -->
|
||||
<div class="pt-2 space-y-2">
|
||||
{#if eventHref || locText || item.attachment?.src}
|
||||
{#if item._slug || eventHref || locText || item.attachment?.src}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#if item._slug}
|
||||
<a
|
||||
href={`/termin/${encodeURIComponent(item._slug)}/`}
|
||||
class="btn-tertiary btn-sm"
|
||||
>
|
||||
Details
|
||||
<Icon
|
||||
icon="mdi:arrow-right"
|
||||
class="size-3.5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
{#if eventHref}
|
||||
<a
|
||||
href={eventHref}
|
||||
@@ -902,6 +928,16 @@
|
||||
Bild
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => openQr(item)}
|
||||
class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white"
|
||||
title="QR-Code anzeigen & herunterladen"
|
||||
aria-label="QR-Code anzeigen & herunterladen"
|
||||
>
|
||||
<Icon icon="mdi:qrcode" class="size-3.5" />
|
||||
QR
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1325,6 +1361,15 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if qrModal}
|
||||
<QrModal
|
||||
value={qrModal.value}
|
||||
label={qrModal.label}
|
||||
sublabel="Termin-Link"
|
||||
onclose={() => (qrModal = null)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
/* Heute-Markierung im Kalendergrid: dezenter Rand + leichtes Glow.
|
||||
Ergänzt das Event-Badge, damit auch leere Tage als "Heute"
|
||||
|
||||
Reference in New Issue
Block a user