fOERbico/docs/relilab-nostr-formular.html

1010 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>relilab Session einreichen</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Yanone+Kaffeesatz:wght@300;400;500;600;700&family=Source+Sans+3:wght@300;400;500;600&display=swap" rel="stylesheet">
<!-- EasyMDE Markdown Editor -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/easymde/2.18.0/easymde.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/easymde/2.18.0/easymde.min.js" defer></script>
<style>
:root {
--rl-blue: #34b2f6;
--rl-purple: #d225f8;
--rl-orange: #ff8103;
--rl-pink: #e54d9a;
--rl-dark: #1a1a2e;
--rl-card: #ffffff;
--rl-bg: #f4f6fb;
--rl-text: #2d2d3a;
--rl-muted: #7a7a8e;
--rl-border: #e0e4ef;
--rl-success: #22c55e;
--rl-error: #ef4444;
--gradient-main: linear-gradient(135deg, var(--rl-blue), var(--rl-purple), var(--rl-pink), var(--rl-orange));
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Source Sans 3', sans-serif;
background: var(--rl-bg);
color: var(--rl-text);
min-height: 100vh;
line-height: 1.6;
}
/* ====== HEADER ====== */
.header {
background: var(--rl-dark);
position: relative;
overflow: hidden;
padding: 2rem 1.5rem 3rem;
text-align: center;
}
.header::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: var(--gradient-main);
opacity: 0.12;
}
.header::after {
content: '';
position: absolute;
bottom: -2px; left: 0; right: 0;
height: 40px;
background: var(--rl-bg);
clip-path: ellipse(55% 100% at 50% 100%);
}
.header-logo {
position: relative;
z-index: 1;
height: 64px;
margin-bottom: 1rem;
filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));
}
.header h1 {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-weight: 700;
font-size: 2.4rem;
color: #fff;
position: relative;
z-index: 1;
letter-spacing: 0.02em;
}
.header h1 span {
background: var(--gradient-main);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.header p {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-weight: 300;
font-size: 1.25rem;
color: rgba(255,255,255,0.7);
position: relative;
z-index: 1;
margin-top: 0.25rem;
}
/* ====== MAIN CONTAINER ====== */
.container {
max-width: 740px;
margin: -1rem auto 3rem;
padding: 0 1.25rem;
position: relative;
z-index: 2;
}
/* ====== SECTIONS ====== */
.form-section {
background: var(--rl-card);
border-radius: 16px;
padding: 2rem 2rem 1.5rem;
margin-bottom: 1.25rem;
box-shadow: 0 1px 4px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.04);
border: 1px solid var(--rl-border);
transition: box-shadow 0.3s ease;
}
.form-section:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.06), 0 12px 32px rgba(0,0,0,0.06);
}
.section-label {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-weight: 600;
font-size: 1.35rem;
color: var(--rl-dark);
margin-bottom: 1.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.section-label .icon {
width: 28px;
height: 28px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.95rem;
flex-shrink: 0;
}
.icon-blue { background: rgba(52,178,246,0.12); color: var(--rl-blue); }
.icon-purple { background: rgba(210,37,248,0.12); color: var(--rl-purple); }
.icon-orange { background: rgba(255,129,3,0.12); color: var(--rl-orange); }
.icon-pink { background: rgba(229,77,154,0.12); color: var(--rl-pink); }
/* ====== FORM ELEMENTS ====== */
.field { margin-bottom: 1.25rem; }
.field:last-child { margin-bottom: 0; }
label {
display: block;
font-weight: 500;
font-size: 0.92rem;
color: var(--rl-text);
margin-bottom: 0.35rem;
}
label .optional {
font-weight: 400;
color: var(--rl-muted);
font-size: 0.82rem;
}
label .required {
color: var(--rl-pink);
}
input[type="text"],
input[type="email"],
input[type="url"],
input[type="date"],
input[type="time"],
input[type="password"],
select {
width: 100%;
padding: 0.65rem 0.9rem;
border: 1.5px solid var(--rl-border);
border-radius: 10px;
font-family: 'Source Sans 3', sans-serif;
font-size: 0.95rem;
color: var(--rl-text);
background: #fff;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
input:focus, select:focus {
border-color: var(--rl-blue);
box-shadow: 0 0 0 3px rgba(52,178,246,0.12);
}
input::placeholder { color: var(--rl-muted); opacity: 0.7; }
.row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
@media (max-width: 520px) {
.row { grid-template-columns: 1fr; }
}
/* ====== FILE UPLOAD ====== */
.file-drop {
border: 2px dashed var(--rl-border);
border-radius: 12px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: border-color 0.2s, background 0.2s;
position: relative;
}
.file-drop:hover, .file-drop.dragover {
border-color: var(--rl-blue);
background: rgba(52,178,246,0.04);
}
.file-drop.has-file {
border-color: var(--rl-success);
background: rgba(34,197,94,0.04);
}
.file-drop input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
.file-drop-icon { font-size: 2rem; margin-bottom: 0.5rem; }
.file-drop-text { font-size: 0.9rem; color: var(--rl-muted); }
.file-drop-text strong { color: var(--rl-blue); }
.file-preview {
margin-top: 1rem;
display: none;
}
.file-preview img {
max-width: 100%;
max-height: 200px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.file-preview .file-name {
font-size: 0.85rem;
color: var(--rl-muted);
margin-top: 0.5rem;
}
.file-remove {
display: inline-block;
margin-top: 0.5rem;
font-size: 0.82rem;
color: var(--rl-error);
cursor: pointer;
text-decoration: underline;
background: none;
border: none;
font-family: inherit;
}
/* ====== CC LICENSE SELECT ====== */
.license-grid {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.license-option {
display: flex;
align-items: center;
gap: 0.65rem;
padding: 0.55rem 0.85rem;
border-radius: 10px;
cursor: pointer;
border: 1.5px solid transparent;
transition: all 0.2s;
font-size: 0.9rem;
}
.license-option:hover {
background: rgba(0,0,0,0.02);
}
.license-option input[type="radio"] {
accent-color: var(--rl-blue);
width: 16px;
height: 16px;
flex-shrink: 0;
}
.license-option.selected {
border-color: var(--rl-blue);
background: rgba(52,178,246,0.06);
}
.license-green {
background: rgba(34,197,94,0.06);
border-color: rgba(34,197,94,0.2);
}
.license-green:hover { background: rgba(34,197,94,0.1); }
.license-green.selected {
border-color: var(--rl-success);
background: rgba(34,197,94,0.12);
}
.license-red {
background: rgba(239,68,68,0.04);
border-color: rgba(239,68,68,0.15);
}
.license-red:hover { background: rgba(239,68,68,0.08); }
.license-red.selected {
border-color: var(--rl-error);
background: rgba(239,68,68,0.1);
}
.license-badge {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-weight: 600;
font-size: 0.72rem;
padding: 0.15rem 0.45rem;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.04em;
flex-shrink: 0;
}
.badge-green { background: rgba(34,197,94,0.15); color: #16a34a; }
.badge-red { background: rgba(239,68,68,0.12); color: #dc2626; }
/* ====== CHECKBOX GRID (Zielgruppe) ====== */
.checkbox-grid {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.chip {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.4rem 0.85rem;
border-radius: 50px;
border: 1.5px solid var(--rl-border);
cursor: pointer;
font-size: 0.88rem;
transition: all 0.2s;
user-select: none;
}
.chip:hover { border-color: var(--rl-purple); background: rgba(210,37,248,0.04); }
.chip input[type="checkbox"] { display: none; }
.chip.active {
border-color: var(--rl-purple);
background: rgba(210,37,248,0.1);
color: var(--rl-purple);
font-weight: 500;
}
.chip .checkmark { font-size: 0.75rem; display: none; }
.chip.active .checkmark { display: inline; }
/* ====== TOGGLE / VIDEOKONFERENZ ====== */
.toggle-row {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
}
.toggle {
position: relative;
width: 44px;
height: 24px;
flex-shrink: 0;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: var(--rl-border);
border-radius: 24px;
transition: background 0.25s;
}
.toggle-slider::before {
content: '';
position: absolute;
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background: white;
border-radius: 50%;
transition: transform 0.25s;
box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}
.toggle input:checked + .toggle-slider {
background: var(--rl-orange);
}
.toggle input:checked + .toggle-slider::before {
transform: translateX(20px);
}
.toggle-label {
font-size: 0.9rem;
color: var(--rl-text);
}
.custom-url-field {
display: none;
margin-top: 0.75rem;
padding: 0.85rem;
border-radius: 10px;
background: rgba(255,129,3,0.04);
border: 1px solid rgba(255,129,3,0.15);
}
.custom-url-field.visible { display: block; }
.zoom-default {
font-size: 0.85rem;
color: var(--rl-muted);
margin-top: 0.35rem;
}
.zoom-default a { color: var(--rl-blue); text-decoration: none; }
.zoom-default a:hover { text-decoration: underline; }
/* ====== SUBMIT ====== */
.submit-section {
text-align: center;
padding: 1.5rem 2rem 2rem;
}
.submit-btn {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-weight: 700;
font-size: 1.35rem;
letter-spacing: 0.03em;
padding: 0.85rem 3rem;
border: none;
border-radius: 50px;
color: #fff;
background: var(--gradient-main);
background-size: 200% 200%;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 16px rgba(52,178,246,0.25);
position: relative;
overflow: hidden;
}
.submit-btn:hover {
background-position: 100% 0;
box-shadow: 0 6px 24px rgba(210,37,248,0.3);
transform: translateY(-1px);
}
.submit-btn:active { transform: translateY(0); }
.submit-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.submit-btn .spinner {
display: none;
width: 20px;
height: 20px;
border: 2.5px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.7s linear infinite;
margin-right: 0.5rem;
vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }
.submit-btn.loading .spinner { display: inline-block; }
.submit-btn.loading .btn-text { opacity: 0.8; }
/* ====== STATUS MESSAGES ====== */
.status-msg {
margin-top: 1.25rem;
padding: 1rem 1.25rem;
border-radius: 12px;
font-size: 0.92rem;
display: none;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.status-msg.success {
display: block;
background: rgba(34,197,94,0.08);
border: 1px solid rgba(34,197,94,0.25);
color: #16a34a;
}
.status-msg.error {
display: block;
background: rgba(239,68,68,0.08);
border: 1px solid rgba(239,68,68,0.2);
color: #dc2626;
}
/* ====== EASYMDE OVERRIDES ====== */
.EasyMDEContainer .CodeMirror {
border-radius: 10px;
border-color: var(--rl-border);
font-family: 'Source Sans 3', sans-serif;
font-size: 0.95rem;
min-height: 120px;
}
.EasyMDEContainer .CodeMirror-focused {
border-color: var(--rl-blue);
box-shadow: 0 0 0 3px rgba(52,178,246,0.12);
}
.EasyMDEContainer .editor-toolbar {
border-color: var(--rl-border);
border-radius: 10px 10px 0 0;
}
.EasyMDEContainer .editor-toolbar button:hover {
background: rgba(52,178,246,0.08);
}
.editor-toolbar button.active, .editor-toolbar button:hover {
border-color: var(--rl-blue);
}
/* ====== FOOTER ====== */
.footer {
text-align: center;
padding: 1.5rem;
font-size: 0.8rem;
color: var(--rl-muted);
}
.footer a { color: var(--rl-blue); text-decoration: none; }
/* ====== RESPONSIVE ====== */
@media (max-width: 520px) {
.header h1 { font-size: 1.8rem; }
.form-section { padding: 1.5rem 1.25rem 1.25rem; }
.submit-btn { width: 100%; }
}
</style>
</head>
<body>
<!-- ====== HEADER ====== -->
<header class="header">
<img src="https://relilab.org/wp-content/uploads/2021/06/Design-ohne-Titel.png" alt="relilab Logo" class="header-logo">
<h1><span>Session</span> einreichen</h1>
<p>Erstelle ein Nostr-Kalender-Event für das relilab</p>
</header>
<!-- ====== FORM ====== -->
<main class="container">
<form id="eventForm" novalidate>
<!-- KONTAKT -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-blue"></span>
Kontakt
</div>
<div class="field">
<label for="email">E-Mail-Adresse <span class="optional">(optional)</span></label>
<input type="email" id="email" name="email" placeholder="deine@email.de">
</div>
</div>
<!-- SESSION -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-purple">📋</span>
Session-Details
</div>
<div class="field">
<label for="title">Session-Titel <span class="required">*</span></label>
<input type="text" id="title" name="title" placeholder="z.B. KI im Religionsunterricht" required>
</div>
<div class="field">
<label for="summary">Kurzbeschreibung <span class="optional">(optional)</span></label>
<textarea id="summary" name="summary"></textarea>
</div>
<div class="field">
<label for="content">Inhaltsbeschreibung <span class="optional">(optional)</span></label>
<textarea id="content" name="content"></textarea>
</div>
</div>
<!-- BILD -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-orange">🖼</span>
Beitragsbild
</div>
<div class="field">
<div class="file-drop" id="fileDrop">
<input type="file" id="image" name="image" accept="image/jpeg,image/png,image/webp">
<div class="file-drop-icon">📷</div>
<div class="file-drop-text">Bild hierher ziehen oder <strong>klicken</strong> zum Auswählen<br><small>JPG, PNG oder WebP</small></div>
</div>
<div class="file-preview" id="filePreview">
<img id="previewImg" src="" alt="Vorschau">
<div class="file-name" id="fileName"></div>
<button type="button" class="file-remove" id="fileRemove">Bild entfernen</button>
</div>
</div>
<div id="imageMetaFields" style="display:none;">
<div class="field">
<label for="imageCredit">Bildquelle / Credit <span class="optional">(optional)</span></label>
<input type="text" id="imageCredit" name="imageCredit" placeholder="z.B. Foto: Max Mustermann">
</div>
<div class="field">
<label>Creative-Commons-Lizenz <span class="required">*</span></label>
<div class="license-grid" id="licenseGrid">
<!-- Grün (empfohlen) -->
<label class="license-option license-green">
<input type="radio" name="license" value="CC0">
<span class="license-badge badge-green">empfohlen</span>
<span><strong>CC0</strong> Public Domain / Keine Rechte vorbehalten</span>
</label>
<label class="license-option license-green">
<input type="radio" name="license" value="CC BY 4.0">
<span class="license-badge badge-green">empfohlen</span>
<span><strong>CC BY</strong> Namensnennung</span>
</label>
<label class="license-option license-green">
<input type="radio" name="license" value="CC BY-SA 4.0">
<span class="license-badge badge-green">empfohlen</span>
<span><strong>CC BY-SA</strong> Namensnennung, Weitergabe unter gleichen Bedingungen</span>
</label>
<!-- Rot (einschränkend) -->
<label class="license-option license-red">
<input type="radio" name="license" value="CC BY-NC 4.0">
<span class="license-badge badge-red">einschränkend</span>
<span><strong>CC BY-NC</strong> Nicht kommerziell</span>
</label>
<label class="license-option license-red">
<input type="radio" name="license" value="CC BY-NC-SA 4.0">
<span class="license-badge badge-red">einschränkend</span>
<span><strong>CC BY-NC-SA</strong> Nicht kommerziell, Weitergabe unter gleichen Bed.</span>
</label>
<label class="license-option license-red">
<input type="radio" name="license" value="CC BY-ND 4.0">
<span class="license-badge badge-red">einschränkend</span>
<span><strong>CC BY-ND</strong> Keine Bearbeitung</span>
</label>
<label class="license-option license-red">
<input type="radio" name="license" value="CC BY-NC-ND 4.0">
<span class="license-badge badge-red">einschränkend</span>
<span><strong>CC BY-NC-ND</strong> Nicht kommerziell, Keine Bearbeitung</span>
</label>
</div>
</div>
</div>
</div>
<!-- DATUM & ZEIT -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-pink">📅</span>
Datum & Uhrzeit
</div>
<div class="row">
<div class="field">
<label for="startDate">Start-Datum <span class="required">*</span></label>
<input type="date" id="startDate" name="startDate" required>
</div>
<div class="field">
<label for="startTime">Start-Uhrzeit <span class="required">*</span></label>
<input type="time" id="startTime" name="startTime" required>
</div>
</div>
<div class="row">
<div class="field">
<label for="endDate">End-Datum <span class="required">*</span></label>
<input type="date" id="endDate" name="endDate" required>
</div>
<div class="field">
<label for="endTime">End-Uhrzeit <span class="required">*</span></label>
<input type="time" id="endTime" name="endTime" required>
</div>
</div>
</div>
<!-- VIDEOKONFERENZ -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-blue">🎥</span>
Videokonferenz
</div>
<div class="zoom-default">
Standard: <a href="https://relilab.org/live/" target="_blank" rel="noopener">relilab-Zoom (relilab.org/live)</a>
</div>
<div class="toggle-row" style="margin-top: 0.75rem;">
<label class="toggle">
<input type="checkbox" id="customZoom">
<span class="toggle-slider"></span>
</label>
<span class="toggle-label">Veranstaltung findet <strong>nicht</strong> im relilab-Zoom statt</span>
</div>
<div class="custom-url-field" id="customUrlField">
<div class="field">
<label for="customUrl">Videokonferenz-URL <span class="required">*</span></label>
<input type="url" id="customUrl" name="customUrl" placeholder="https://meet.example.com/...">
</div>
</div>
</div>
<!-- ZIELGRUPPE -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-purple">🎯</span>
Zielgruppe
</div>
<div class="checkbox-grid" id="audienceGrid">
<label class="chip"><input type="checkbox" name="audience" value="elementar"><span class="checkmark"></span> Elementar</label>
<label class="chip"><input type="checkbox" name="audience" value="Grundschule"><span class="checkmark"></span> Grundschule</label>
<label class="chip"><input type="checkbox" name="audience" value="Sek1"><span class="checkmark"></span> Sek 1</label>
<label class="chip"><input type="checkbox" name="audience" value="Sek2"><span class="checkmark"></span> Sek 2</label>
<label class="chip"><input type="checkbox" name="audience" value="BRU"><span class="checkmark"></span> BRU</label>
<label class="chip"><input type="checkbox" name="audience" value="Erwachsenenbildung"><span class="checkmark"></span> Erwachsenenbildung</label>
<label class="chip"><input type="checkbox" name="audience" value="Hochschule"><span class="checkmark"></span> Hochschule</label>
</div>
</div>
<!-- HASHTAGS -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-orange">🏷</span>
Hashtags / Kategorien
</div>
<div class="field">
<label for="hashtags">Hashtags <span class="optional">(kommagetrennt, optional)</span></label>
<input type="text" id="hashtags" name="hashtags" placeholder="z.B. ki, religionsunterricht, digital">
</div>
</div>
<!-- AUTH -->
<div class="form-section">
<div class="section-label">
<span class="icon icon-pink">🔑</span>
Authentifizierung
</div>
<div class="field">
<label for="token">Zugangs-Token <span class="required">*</span></label>
<input type="password" id="token" name="token" placeholder="Dein Passwort / Token" required>
</div>
</div>
<!-- SUBMIT -->
<div class="form-section submit-section">
<button type="submit" class="submit-btn" id="submitBtn">
<span class="spinner"></span>
<span class="btn-text">Session veröffentlichen</span>
</button>
<div class="status-msg" id="statusMsg"></div>
</div>
</form>
</main>
<footer class="footer">
relilab Religionspädagogisches Labor · Powered by <a href="https://nostr.com" target="_blank" rel="noopener">Nostr</a>
</footer>
<script>
// ============================================
// KONFIGURATION Webhook-URL hier eintragen!
// ============================================
const WEBHOOK_URL = 'https://DEIN-N8N-SERVER.example.com/webhook/nostr-calendar-event';
document.addEventListener('DOMContentLoaded', () => {
// ============================================
// WYSIWYG Editors (EasyMDE)
// ============================================
let summaryEditor, contentEditor;
function initEditors() {
if (typeof EasyMDE === 'undefined') {
// Fallback: retry after a short delay
setTimeout(initEditors, 200);
return;
}
summaryEditor = new EasyMDE({
element: document.getElementById('summary'),
placeholder: 'Kurzer Teaser-Text zur Session…',
spellChecker: false,
status: false,
toolbar: ['bold', 'italic', 'link', '|', 'preview'],
minHeight: '80px',
});
contentEditor = new EasyMDE({
element: document.getElementById('content'),
placeholder: 'Ausführliche Beschreibung der Session-Inhalte…',
spellChecker: false,
status: ['lines', 'words'],
toolbar: ['bold', 'italic', 'heading', '|', 'unordered-list', 'ordered-list', '|', 'link', 'image', '|', 'preview', 'side-by-side'],
minHeight: '160px',
});
}
initEditors();
// ============================================
// AUTO-FILL: Start → End
// ============================================
const startDate = document.getElementById('startDate');
const startTime = document.getElementById('startTime');
const endDate = document.getElementById('endDate');
const endTime = document.getElementById('endTime');
let endManuallyEdited = false;
startDate.addEventListener('change', () => {
if (!endManuallyEdited || endDate.value === '' || endDate.value === startDate.dataset.prev) {
endDate.value = startDate.value;
}
startDate.dataset.prev = startDate.value;
});
startTime.addEventListener('change', () => {
if (!endManuallyEdited || endTime.value === '' || endTime.value === startTime.dataset.prev) {
endTime.value = startTime.value;
}
startTime.dataset.prev = startTime.value;
});
endDate.addEventListener('input', () => { endManuallyEdited = true; });
endTime.addEventListener('input', () => { endManuallyEdited = true; });
// ============================================
// FILE UPLOAD / PREVIEW
// ============================================
const fileDrop = document.getElementById('fileDrop');
const fileInput = document.getElementById('image');
const filePreview = document.getElementById('filePreview');
const previewImg = document.getElementById('previewImg');
const fileNameEl = document.getElementById('fileName');
const fileRemove = document.getElementById('fileRemove');
const imageMetaFields = document.getElementById('imageMetaFields');
function showImagePreview(file) {
const reader = new FileReader();
reader.onload = (e) => {
previewImg.src = e.target.result;
fileNameEl.textContent = file.name + ' (' + (file.size / 1024).toFixed(0) + ' KB)';
filePreview.style.display = 'block';
fileDrop.classList.add('has-file');
imageMetaFields.style.display = 'block';
};
reader.readAsDataURL(file);
}
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) showImagePreview(e.target.files[0]);
});
fileDrop.addEventListener('dragover', (e) => { e.preventDefault(); fileDrop.classList.add('dragover'); });
fileDrop.addEventListener('dragleave', () => { fileDrop.classList.remove('dragover'); });
fileDrop.addEventListener('drop', (e) => {
e.preventDefault();
fileDrop.classList.remove('dragover');
if (e.dataTransfer.files.length > 0) {
fileInput.files = e.dataTransfer.files;
showImagePreview(e.dataTransfer.files[0]);
}
});
fileRemove.addEventListener('click', () => {
fileInput.value = '';
filePreview.style.display = 'none';
fileDrop.classList.remove('has-file');
imageMetaFields.style.display = 'none';
});
// ============================================
// LICENSE SELECTION HIGHLIGHT
// ============================================
document.querySelectorAll('#licenseGrid .license-option').forEach(opt => {
const radio = opt.querySelector('input[type="radio"]');
radio.addEventListener('change', () => {
document.querySelectorAll('#licenseGrid .license-option').forEach(o => o.classList.remove('selected'));
opt.classList.add('selected');
});
});
// ============================================
// AUDIENCE CHIPS
// ============================================
document.querySelectorAll('#audienceGrid .chip').forEach(chip => {
chip.addEventListener('click', (e) => {
// Let the checkbox toggle naturally, then update class
setTimeout(() => {
const cb = chip.querySelector('input[type="checkbox"]');
chip.classList.toggle('active', cb.checked);
}, 0);
});
});
// ============================================
// CUSTOM ZOOM TOGGLE
// ============================================
const customZoom = document.getElementById('customZoom');
const customUrlField = document.getElementById('customUrlField');
customZoom.addEventListener('change', () => {
customUrlField.classList.toggle('visible', customZoom.checked);
if (!customZoom.checked) {
document.getElementById('customUrl').value = '';
}
});
// ============================================
// FORM SUBMISSION
// ============================================
const form = document.getElementById('eventForm');
const submitBtn = document.getElementById('submitBtn');
const statusMsg = document.getElementById('statusMsg');
form.addEventListener('submit', async (e) => {
e.preventDefault();
statusMsg.className = 'status-msg';
statusMsg.style.display = 'none';
// Validation
const title = document.getElementById('title').value.trim();
const sDate = startDate.value;
const sTime = startTime.value;
const eDate = endDate.value;
const eTime = endTime.value;
const token = document.getElementById('token').value;
if (!title) return showError('Bitte gib einen Session-Titel ein.');
if (!sDate || !sTime) return showError('Bitte gib Start-Datum und -Uhrzeit ein.');
if (!eDate || !eTime) return showError('Bitte gib End-Datum und -Uhrzeit ein.');
if (!token) return showError('Bitte gib den Zugangs-Token ein.');
// Check end > start
const startTs = new Date(sDate + 'T' + sTime).getTime() / 1000;
const endTs = new Date(eDate + 'T' + eTime).getTime() / 1000;
if (endTs < startTs) return showError('Das End-Datum/Zeit muss nach dem Start liegen.');
// Custom URL required?
if (customZoom.checked && !document.getElementById('customUrl').value.trim()) {
return showError('Bitte gib eine Videokonferenz-URL ein.');
}
// License required if image?
if (fileInput.files.length > 0) {
const selectedLicense = document.querySelector('input[name="license"]:checked');
if (!selectedLicense) return showError('Bitte wähle eine Creative-Commons-Lizenz für das Bild.');
}
// Build payload
const audiences = Array.from(document.querySelectorAll('input[name="audience"]:checked')).map(cb => cb.value);
const hashtagsRaw = document.getElementById('hashtags').value.trim();
const hashtags = hashtagsRaw ? hashtagsRaw.split(',').map(t => t.trim().toLowerCase()).filter(Boolean) : [];
const payload = {
token: token,
email: document.getElementById('email').value.trim(),
title: title,
summary: summaryEditor ? summaryEditor.value() : document.getElementById('summary').value,
content: contentEditor ? contentEditor.value() : document.getElementById('content').value,
startTimestamp: startTs,
endTimestamp: endTs,
location: customZoom.checked ? document.getElementById('customUrl').value.trim() : 'https://relilab.org/live/',
audiences: audiences,
hashtags: hashtags,
imageCredit: document.getElementById('imageCredit').value.trim(),
license: fileInput.files.length > 0 ? document.querySelector('input[name="license"]:checked')?.value || '' : '',
};
// Build FormData (for image upload)
const formData = new FormData();
formData.append('data', JSON.stringify(payload));
if (fileInput.files.length > 0) {
formData.append('image', fileInput.files[0]);
}
// Submit
submitBtn.disabled = true;
submitBtn.classList.add('loading');
try {
const res = await fetch(WEBHOOK_URL, {
method: 'POST',
body: formData,
});
const result = await res.json();
if (res.ok && result.success) {
showSuccess('Session erfolgreich veröffentlicht! 🎉' + (result.eventId ? '<br><small>Event-ID: ' + result.eventId.substring(0, 16) + '…</small>' : ''));
form.reset();
summaryEditor && summaryEditor.value('');
contentEditor && contentEditor.value('');
filePreview.style.display = 'none';
fileDrop.classList.remove('has-file');
imageMetaFields.style.display = 'none';
customUrlField.classList.remove('visible');
document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
document.querySelectorAll('.license-option').forEach(o => o.classList.remove('selected'));
endManuallyEdited = false;
} else {
showError(result.error || 'Fehler beim Veröffentlichen. Bitte versuche es erneut.');
}
} catch (err) {
showError('Verbindungsfehler: ' + err.message);
} finally {
submitBtn.disabled = false;
submitBtn.classList.remove('loading');
}
});
function showError(msg) {
statusMsg.className = 'status-msg error';
statusMsg.innerHTML = msg;
statusMsg.style.display = 'block';
statusMsg.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function showSuccess(msg) {
statusMsg.className = 'status-msg success';
statusMsg.innerHTML = msg;
statusMsg.style.display = 'block';
statusMsg.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}); // end DOMContentLoaded
</script>
</body>
</html>