From d04a7e07dec857f5f6b7b3ff6e538f6c7972a204 Mon Sep 17 00:00:00 2001 From: viiivo <«vivien.vonburg@outlook.com»> Date: Fri, 10 Apr 2026 18:16:03 +0200 Subject: [PATCH] feat: Update CSS styles and enhance JavaScript functionality for event visibility and navigation Added Comments for easier understanding of code --- css/event_create.css | 3 +- css/landingpage.css | 25 ++++++++++------ js/event_detail.js | 15 +++++++++- js/index-carousel.js | 18 ++++++++++++ js/landingpage.js | 22 ++++++++++++-- js/login.js | 14 +++++++-- js/my_profil.js | 69 +++++++++++++++++++++++++++++++++++++++++++- js/navigation.js | 6 ++++ js/signup.js | 16 ++++++++-- 9 files changed, 168 insertions(+), 20 deletions(-) diff --git a/css/event_create.css b/css/event_create.css index 5ce1af7..a58da91 100644 --- a/css/event_create.css +++ b/css/event_create.css @@ -39,7 +39,7 @@ html { body { margin: 0; - font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-family: var(--font-main); background: var(--color-bg); color: var(--color-text); line-height: 1.5; @@ -183,6 +183,7 @@ fieldset { h1, h2 { margin: 0; + font-family: "Bagel Fat One", cursive; font-size: clamp(2rem, 4vw, 4rem); line-height: 1.03; letter-spacing: -0.03em; diff --git a/css/landingpage.css b/css/landingpage.css index 3d70ebb..f6ffea2 100644 --- a/css/landingpage.css +++ b/css/landingpage.css @@ -20,15 +20,15 @@ * { box-sizing: border-box; } :root { - --black: #000000; + --black: #22211A; --white: #ffffff; - --button-green: #6b6b05; - --button-green-dark: #514c04; + --button-green: var(--olive); + --button-green-dark: var(--olive-dark); } body { margin: 0; - font-family: 'Inter', sans-serif; + font-family: var(--font-main); background: #FFFDE3; /* butter background color from stylesheet */ } @@ -259,7 +259,7 @@ body { .how-step__png--brown { filter: brightness(0) saturate(100%) invert(39%) sepia(84%) saturate(1682%) hue-rotate(349deg) brightness(93%) contrast(86%); } -} + @media (max-width: 900px) { .how-it-works__steps { @@ -378,7 +378,7 @@ body { } /* Center arrow removed – using side arrows only */ -} + @media (max-width: 900px) { .gallery__track { @@ -405,12 +405,19 @@ body { justify-content: center; border-radius: 999px; font-size: 0.95rem; - transition: background-color 0.2s ease, transform 0.2s ease; + transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease; } -.btn:hover { +.btn:hover, +.btn:focus-visible { background-color: var(--button-green-dark); transform: translateY(-1px); + box-shadow: 0 4px 10px rgba(107, 107, 5, 0.28); +} + +.btn:active { + transform: translateY(0); + box-shadow: 0 2px 6px rgba(107, 107, 5, 0.25); } .nav__link { @@ -512,7 +519,7 @@ body { } .gallery__brand { - font-family: 'Inter', sans-serif; + font-family: var(--font-main); color: #DD541A; } diff --git a/js/event_detail.js b/js/event_detail.js index 2ca06ac..8481cc7 100644 --- a/js/event_detail.js +++ b/js/event_detail.js @@ -117,6 +117,19 @@ document.addEventListener('DOMContentLoaded', async () => { return msUntilStart <= twelveHoursInMs; } + // Adresse ist nur im 12h-Fenster VOR Eventstart sichtbar. + function isAddressVisibleWindow(event) { + const eventDateTime = parseEventDateTime(event); + if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) { + return false; + } + + const msUntilStart = eventDateTime.getTime() - Date.now(); + const twelveHoursInMs = 12 * 60 * 60 * 1000; + + return msUntilStart >= 0 && msUntilStart <= twelveHoursInMs; + } + function countRegistrationsForEvent(registrationMap, eventId) { return Object.values(registrationMap).reduce((count, ids) => { const hasEvent = Array.isArray(ids) @@ -286,7 +299,7 @@ document.addEventListener('DOMContentLoaded', async () => { : isRegistrationClosed ? ' detail-primary-btn-danger' : ' detail-primary-btn-register'; - const shouldRevealAddress = Boolean(event.address) && isRegistrationClosed && hasAddressAccess; + const shouldRevealAddress = Boolean(event.address) && isAddressVisibleWindow(event) && hasAddressAccess; const addressPanelMarkup = shouldRevealAddress ? `
diff --git a/js/index-carousel.js b/js/index-carousel.js index 42128fc..e16100d 100644 --- a/js/index-carousel.js +++ b/js/index-carousel.js @@ -1,30 +1,45 @@ +// ============================================= +// Galerie-Karussell (Startseite) +// Diese Datei steuert die Foto-Galerie mit Pfeilen. +// ============================================= + +// Wichtige Elemente aus dem HTML holen. const carouselTrack = document.querySelector('.gallery__track'); const prevArrow = document.querySelector('.gallery__arrow--prev'); const nextArrow = document.querySelector('.gallery__arrow--next'); +// Nur ausfuehren, wenn die Galerie auf der Seite vorhanden ist. if (carouselTrack) { + // Alle einzelnen Karten/Bilder im Track sammeln. const items = Array.from(carouselTrack.querySelectorAll('.gallery__item')); + + // Auf Mobile zeigen wir 1 Bild, auf Desktop 3 Bilder pro "Seite". const getItemsPerPage = () => (window.matchMedia('(max-width: 900px)').matches ? 1 : 3); let itemsPerPage = getItemsPerPage(); const pageCount = Math.ceil(items.length / itemsPerPage); let activePage = 0; + // Scrollt den Track auf eine bestimmte Seite. function scrollToPage(page) { activePage = page; const pageWidth = carouselTrack.clientWidth; carouselTrack.scrollTo({ left: pageWidth * page, behavior: 'smooth' }); } + // Geht zur naechsten Seite (mit Wrap-around am Ende). function showNext() { activePage = (activePage + 1) % pageCount; scrollToPage(activePage); } + // Geht zur vorherigen Seite (mit Wrap-around zum Ende). function showPrev() { activePage = (activePage - 1 + pageCount) % pageCount; scrollToPage(activePage); } + // Wenn sich bei Resize die Karten-Anzahl pro Seite aendert, + // laden wir die Seite neu, damit Layout und Seitenzahl wieder stimmen. function refreshCarousel() { const responsiveItemsPerPage = getItemsPerPage(); if (responsiveItemsPerPage !== itemsPerPage) { @@ -33,9 +48,11 @@ if (carouselTrack) { } } + // Klick-Steuerung der Pfeile. if (nextArrow) nextArrow.addEventListener('click', showNext); if (prevArrow) prevArrow.addEventListener('click', showPrev); + // Tastatur-Support fuer Barrierefreiheit. document.addEventListener('keydown', (event) => { if (event.key === 'ArrowRight') { showNext(); @@ -45,6 +62,7 @@ if (carouselTrack) { } }); + // Reagiert auf Bildschirmgroessen-Aenderungen. window.addEventListener('resize', () => { refreshCarousel(); scrollToPage(activePage); diff --git a/js/landingpage.js b/js/landingpage.js index c70097e..46c015c 100644 --- a/js/landingpage.js +++ b/js/landingpage.js @@ -1,8 +1,18 @@ +// ============================================= +// Mini-Galerie auf der Landingpage +// Diese Datei hebt immer ein Bild hervor und +// erlaubt Navigation mit Pfeilen/Tastatur. +// ============================================= + +// Elemente aus dem DOM lesen. const prevBtn = document.querySelector('.arrow--prev'); const nextBtn = document.querySelector('.arrow--next'); const items = Array.from(document.querySelectorAll('.gallery__item')); let activeIndex = 0; +// Aktualisiert die Darstellung aller Bilder: +// - aktives Bild ist klar sichtbar +// - inaktive Bilder sind abgeblendet function updateGallery() { items.forEach((item, i) => { item.style.opacity = i === activeIndex ? '1' : '0.35'; @@ -10,20 +20,25 @@ function updateGallery() { }); } +// Ein Schritt nach rechts. function showNext() { + if (!items.length) return; activeIndex = (activeIndex + 1) % items.length; updateGallery(); } +// Ein Schritt nach links. function showPrev() { + if (!items.length) return; activeIndex = (activeIndex - 1 + items.length) % items.length; updateGallery(); } -nextBtn.addEventListener('click', showNext); -prevBtn.addEventListener('click', showPrev); +// Event-Handler nur registrieren, wenn die Buttons existieren. +if (nextBtn) nextBtn.addEventListener('click', showNext); +if (prevBtn) prevBtn.addEventListener('click', showPrev); -// keyboard support +// Tastatursteuerung fuer bessere Bedienbarkeit. document.addEventListener('keydown', (event) => { if (event.key === 'ArrowRight') { showNext(); @@ -33,4 +48,5 @@ document.addEventListener('keydown', (event) => { } }); +// Initialen Zustand einmal setzen. updateGallery(); \ No newline at end of file diff --git a/js/login.js b/js/login.js index 5ba97fe..3b2ed50 100644 --- a/js/login.js +++ b/js/login.js @@ -1,3 +1,10 @@ +// ============================================= +// Login-Logik +// Diese Datei validiert die Eingaben, sucht den +// Benutzer im localStorage und legt die Session an. +// ============================================= + +// Formular und Felder aus dem HTML holen. const loginForm = document.getElementById('loginForm'); const emailInput = document.getElementById('email'); const passwortInput = document.getElementById('passwort'); @@ -79,7 +86,10 @@ function validateForm(event) { passwortGroup.classList.remove('has-error'); } - // Wenn alle Validierungen bestanden, Benutzer pruefen und Session speichern. + // Wenn alle Validierungen bestanden, pruefen wir: + // 1) gibt es den Benutzer schon? + // 2) ist das Passwort korrekt? + // Danach speichern wir die aktive Session. if (isValid) { const users = getStoredUsers(); const matchedUser = users.find(user => user.email?.toLowerCase() === emailValue.toLowerCase()); @@ -93,7 +103,7 @@ function validateForm(event) { const userToLogin = matchedUser || createFallbackUser(emailValue, passwortValue); setCurrentUser(userToLogin); - // Weiterleitung zur Event-Overview-Seite. + // Nach erfolgreichem Login geht es zur Event-Uebersicht. window.location.href = 'event_overview.html'; } } diff --git a/js/my_profil.js b/js/my_profil.js index 9d47a5b..68cfdb4 100644 --- a/js/my_profil.js +++ b/js/my_profil.js @@ -452,7 +452,7 @@ document.addEventListener('DOMContentLoaded', () => { const card = document.createElement('article'); card.className = 'profile-event-card profile-event-card-clickable'; card.setAttribute('data-event-id', String(event.id)); - const addressMarkup = mode === 'registrations' && event.address + const addressMarkup = mode === 'registrations' && event.address && isAddressVisibleWindow(event) ? `

Adresse

@@ -486,6 +486,73 @@ document.addEventListener('DOMContentLoaded', () => { }); } + // Gibt true zurueck, wenn ein Event innerhalb der naechsten 12 Stunden startet. + function isAddressVisibleWindow(event) { + const eventDateTime = parseEventDateTime(event); + if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) { + return false; + } + + const msUntilStart = eventDateTime.getTime() - Date.now(); + const twelveHoursInMs = 12 * 60 * 60 * 1000; + + return msUntilStart >= 0 && msUntilStart <= twelveHoursInMs; + } + + // Parse fuer ISO- und lokalisierte Datumsformate aus den Eventdaten. + function parseEventDateTime(event) { + if (!event?.date) { + return null; + } + + const dateValue = String(event.date).trim(); + const isoDateMatch = dateValue.match(/^(\d{4})-(\d{2})-(\d{2})$/); + let year; + let month; + let day; + + if (isoDateMatch) { + year = Number(isoDateMatch[1]); + month = Number(isoDateMatch[2]); + day = Number(isoDateMatch[3]); + } else { + const monthMap = { + JAN: 1, + FEB: 2, + 'MÄR': 3, + MRZ: 3, + APR: 4, + MAI: 5, + JUN: 6, + JUL: 7, + AUG: 8, + SEP: 9, + OKT: 10, + NOV: 11, + DEZ: 12 + }; + const localizedMatch = dateValue.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/); + + if (!localizedMatch) { + return null; + } + + day = Number(localizedMatch[1]); + month = monthMap[localizedMatch[2]]; + year = Number(localizedMatch[3]); + + if (!month) { + return null; + } + } + + const timeMatch = String(event.time || '').match(/(\d{1,2}):(\d{2})/); + const hours = timeMatch ? Number(timeMatch[1]) : 0; + const minutes = timeMatch ? Number(timeMatch[2]) : 0; + + return new Date(year, month - 1, day, hours, minutes, 0, 0); + } + // Formatiert ein Eventdatum konsistent fuer die Profilkarten. function formatEventDate(dateString) { if (!dateString) { diff --git a/js/navigation.js b/js/navigation.js index ffe8264..08a4b95 100644 --- a/js/navigation.js +++ b/js/navigation.js @@ -1,3 +1,9 @@ +// ============================================= +// Dynamische Navigation +// Je nach Login-Status wird die Kopfzeile fuer +// alle Seiten mit passendem Markup aufgebaut. +// ============================================= + document.addEventListener('DOMContentLoaded', () => { const CURRENT_USER_KEY = 'socialCookingCurrentUser'; const navContainers = document.querySelectorAll('.nav-tab-links'); diff --git a/js/signup.js b/js/signup.js index 87a1b64..21a6fc0 100644 --- a/js/signup.js +++ b/js/signup.js @@ -1,3 +1,10 @@ +// ============================================= +// Signup-Logik +// Diese Datei validiert das Formular, speichert +// neue Benutzer lokal und startet direkt die Session. +// ============================================= + +// Formular und Felder aus dem HTML holen. const signupForm = document.getElementById('signupForm'); const vornameInput = document.getElementById('vorname'); const nachnameInput = document.getElementById('nachname'); @@ -41,7 +48,7 @@ function closeWelcomeModal() { document.body.style.overflow = 'auto'; } -// Validierungsfunktion +// Hauptfunktion fuer Formularvalidierung und Speicherung. function validateForm(event) { event.preventDefault(); @@ -102,7 +109,10 @@ function validateForm(event) { passwortGroup.classList.remove('has-error'); } - // Wenn alle Validierungen bestanden, Benutzer speichern und Session setzen. + // Wenn alles gueltig ist: + // 1) auf doppelte E-Mail pruefen + // 2) neuen Benutzer speichern + // 3) als aktuellen Benutzer einloggen if (isValid) { const existingUsers = getStoredUsers(); const emailLower = emailValue.toLowerCase(); @@ -128,7 +138,7 @@ function validateForm(event) { setCurrentUser(newUser); openWelcomeModal(); - // Hier würde spaeter die Registrierung zum Backend gesendet. + // Hier koennte spaeter ein echter API-Call zum Backend stehen. // Weiterleitung zur Event-Overview-Seite. window.location.href = 'event_overview.html';