feat: Update CSS styles and enhance JavaScript functionality for event visibility and navigation

Added Comments for easier understanding of code
This commit is contained in:
viiivo 2026-04-10 18:16:03 +02:00
parent c3bea2817c
commit d04a7e07de
9 changed files with 168 additions and 20 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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
? `
<article class="detail-panel detail-panel-compact">

View File

@ -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);

View File

@ -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();

View File

@ -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';
}
}

View File

@ -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)
? `
<div class="profile-event-address-block" aria-label="Event Adresse">
<p class="profile-event-address-label">Adresse</p>
@ -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) {

View File

@ -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');

View File

@ -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';