221 lines
6.9 KiB
JavaScript
221 lines
6.9 KiB
JavaScript
/**
|
||
* eventCard.js – Erstellt interaktive Event-Karten als DOM-Elemente.
|
||
* Jede Karte zeigt Titel, Datum, Veranstaltungsort sowie Speichern- und Einladen-Buttons.
|
||
*/
|
||
|
||
/**
|
||
* Erstellt eine vollständige Event-Karte als <article>-Element.
|
||
* @param {Object} event - Event-Objekt mit id, name, date, time, venue, category
|
||
* @returns {HTMLElement} Fertig aufgebautes Karten-Element
|
||
*/
|
||
export function createEventCard(event) {
|
||
|
||
const article = document.createElement("article");
|
||
article.className = "event-card";
|
||
|
||
// --- TITEL ---
|
||
const title = document.createElement("h3");
|
||
title.className = "event-card__title";
|
||
title.textContent = event.name;
|
||
|
||
// --- DATUM & UHRZEIT formatieren (Schweizer Format: dd.mm.yyyy) ---
|
||
const formattedDate = event.date
|
||
? new Date(event.date).toLocaleDateString("de-CH", {
|
||
day: "2-digit",
|
||
month: "2-digit",
|
||
year: "numeric"
|
||
})
|
||
: "Datum unbekannt";
|
||
|
||
const formattedTime = event.time ? event.time.slice(0, 5) : "";
|
||
const dateTime = formattedTime ? `${formattedDate}, ${formattedTime}` : formattedDate;
|
||
|
||
const date = document.createElement("p");
|
||
date.className = "event-card__date";
|
||
date.textContent = dateTime;
|
||
|
||
// --- VERANSTALTUNGSORT ---
|
||
const venue = document.createElement("p");
|
||
venue.className = "event-card__venue";
|
||
venue.textContent = event.venue || "Ort unbekannt";
|
||
|
||
article.append(title, date, venue);
|
||
|
||
if (event.url) {
|
||
const link = document.createElement("a");
|
||
link.href = event.url;
|
||
link.target = "_blank";
|
||
link.rel = "noopener noreferrer";
|
||
link.className = "btn btn-outline-secondary btn-sm mt-2";
|
||
link.textContent = "Event ansehen";
|
||
article.appendChild(link);
|
||
}
|
||
|
||
// Gäste sehen nur Event-Informationen. Speichern und Einladen sind Login-Funktionen.
|
||
if (!window.currentUser) {
|
||
return article;
|
||
}
|
||
|
||
// --- BUTTON-GRUPPE ---
|
||
const buttonContainer = document.createElement("div");
|
||
buttonContainer.className = "d-flex gap-2 mt-2 flex-wrap";
|
||
|
||
// =========================
|
||
// SPEICHERN-BUTTON
|
||
// Speichert das Event im localStorage, verhindert Duplikate
|
||
// =========================
|
||
const saveBtn = document.createElement("button");
|
||
saveBtn.type = "button";
|
||
saveBtn.className = "btn btn-outline-primary btn-sm";
|
||
|
||
// Prüfen ob bereits gespeichert → Button-Text entsprechend setzen
|
||
const alreadySaved = isAlreadySaved(event.id);
|
||
saveBtn.textContent = alreadySaved ? "Gespeichert ✓" : "Speichern";
|
||
if (alreadySaved) saveBtn.disabled = true;
|
||
|
||
saveBtn.addEventListener("click", () => {
|
||
const saved = JSON.parse(localStorage.getItem("savedEvents") || "[]");
|
||
|
||
// Duplikat-Schutz
|
||
if (saved.find(e => e.id === event.id)) {
|
||
saveBtn.textContent = "Gespeichert ✓";
|
||
saveBtn.disabled = true;
|
||
return;
|
||
}
|
||
|
||
saved.push(event);
|
||
localStorage.setItem("savedEvents", JSON.stringify(saved));
|
||
|
||
// Visuelles Feedback statt alert()
|
||
saveBtn.textContent = "Gespeichert ✓";
|
||
saveBtn.disabled = true;
|
||
saveBtn.classList.replace("btn-outline-primary", "btn-success");
|
||
});
|
||
|
||
// =========================
|
||
// EINLADEN-BUTTON
|
||
// Zeigt ein Inline-Formular an (kein browser-prompt())
|
||
// =========================
|
||
const inviteBtn = document.createElement("button");
|
||
inviteBtn.type = "button";
|
||
inviteBtn.textContent = "Einladen";
|
||
inviteBtn.className = "btn btn-primary btn-sm";
|
||
inviteBtn.disabled = false;
|
||
|
||
inviteBtn.addEventListener("click", () => {
|
||
if (!window.currentUser) {
|
||
showCardMessage(article, "Bitte einloggen um Einladungen zu senden.", "warning");
|
||
return;
|
||
}
|
||
// Formular ein-/ausblenden (Toggle)
|
||
const existing = article.querySelector(".invite-form");
|
||
if (existing) {
|
||
existing.remove();
|
||
} else {
|
||
article.appendChild(createInviteForm(event, article));
|
||
}
|
||
});
|
||
|
||
buttonContainer.append(saveBtn, inviteBtn);
|
||
article.appendChild(buttonContainer);
|
||
|
||
return article;
|
||
}
|
||
|
||
// =========================
|
||
// HILFSFUNKTION – Einladungsformular
|
||
// Erstellt ein kleines Inline-Formular mit Benutzername-Eingabe
|
||
// =========================
|
||
function createInviteForm(event, card) {
|
||
const form = document.createElement("div");
|
||
form.className = "invite-form mt-2 d-flex gap-2 align-items-center flex-wrap";
|
||
|
||
const input = document.createElement("input");
|
||
input.type = "text";
|
||
input.className = "form-control form-control-sm";
|
||
input.placeholder = "Benutzername eingeben";
|
||
input.setAttribute("aria-label", "Benutzername für Einladung");
|
||
|
||
const sendBtn = document.createElement("button");
|
||
sendBtn.type = "button";
|
||
sendBtn.textContent = "Senden";
|
||
sendBtn.className = "btn btn-primary btn-sm";
|
||
|
||
const cancelBtn = document.createElement("button");
|
||
cancelBtn.type = "button";
|
||
cancelBtn.textContent = "Abbrechen";
|
||
cancelBtn.className = "btn btn-outline-secondary btn-sm";
|
||
|
||
cancelBtn.addEventListener("click", () => form.remove());
|
||
|
||
sendBtn.addEventListener("click", async () => {
|
||
const toUser = input.value.trim();
|
||
if (!toUser) {
|
||
input.classList.add("is-invalid");
|
||
return;
|
||
}
|
||
input.classList.remove("is-invalid");
|
||
|
||
sendBtn.disabled = true;
|
||
sendBtn.textContent = "Sende...";
|
||
|
||
try {
|
||
const res = await fetch("http://localhost:3000/api/invitation", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-Username": window.currentUser
|
||
},
|
||
body: JSON.stringify({
|
||
toUser,
|
||
eventId: event.id,
|
||
eventName: event.name,
|
||
eventUrl: event.url || null
|
||
})
|
||
});
|
||
|
||
if (!res.ok) {
|
||
// Fehlermeldung vom Server auslesen (z.B. "Nutzer nicht registriert")
|
||
const errData = await res.json().catch(() => ({}));
|
||
throw new Error(errData.message || `Fehler ${res.status}`);
|
||
}
|
||
|
||
// Formular entfernen und Erfolgsmeldung anzeigen
|
||
form.remove();
|
||
showCardMessage(card, `Einladung an "${toUser}" gesendet.`, "success");
|
||
|
||
} catch (err) {
|
||
sendBtn.disabled = false;
|
||
sendBtn.textContent = "Senden";
|
||
showCardMessage(card, err.message || "Fehler beim Senden der Einladung.", "danger");
|
||
}
|
||
});
|
||
|
||
form.append(input, sendBtn, cancelBtn);
|
||
return form;
|
||
}
|
||
|
||
// =========================
|
||
// HILFSFUNKTION – Status-Nachricht in Karte anzeigen
|
||
// Zeigt eine kurze Meldung innerhalb der Karte an und blendet sie nach 3s aus
|
||
// =========================
|
||
function showCardMessage(card, message, type) {
|
||
// Alte Nachrichten entfernen
|
||
card.querySelectorAll(".card-message").forEach(el => el.remove());
|
||
|
||
const msg = document.createElement("p");
|
||
msg.className = `card-message text-${type} mt-1 mb-0`;
|
||
msg.textContent = message;
|
||
card.appendChild(msg);
|
||
|
||
setTimeout(() => msg.remove(), 3000);
|
||
}
|
||
|
||
// =========================
|
||
// HILFSFUNKTION – Prüft ob Event bereits gespeichert ist
|
||
// =========================
|
||
function isAlreadySaved(eventId) {
|
||
const saved = JSON.parse(localStorage.getItem("savedEvents") || "[]");
|
||
return saved.some(e => e.id === eventId);
|
||
}
|