Add snackbar feedback, logout modal, profile pill nav, calendar styling, fix image paths and signup flow

This commit is contained in:
Estelle Köhler 2026-04-12 14:19:09 +02:00
parent 221aa90649
commit 4b54c48311
11 changed files with 234 additions and 26 deletions

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"chat.tools.terminal.autoApprove": {
"git remote": true,
"git push": true,
"ssh": true,
"git add": true
}
}

View File

@ -147,6 +147,33 @@
outline-offset: 1px;
}
.meta-filter input[type="date"] {
color-scheme: light;
accent-color: var(--olive);
}
.meta-filter input[type="date"]::-webkit-calendar-picker-indicator {
cursor: pointer;
border-radius: 4px;
padding: 4px;
filter: invert(35%) sepia(60%) saturate(600%) hue-rotate(22deg) brightness(90%) contrast(95%);
transition: background-color 0.2s ease;
}
.meta-filter input[type="date"]::-webkit-calendar-picker-indicator:hover {
background-color: var(--olive-light);
}
.meta-filter select {
cursor: pointer;
appearance: none;
-webkit-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236B6B05' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 36px;
}
/* ---------------------------------------------------------
Overview Event Cards
--------------------------------------------------------- */

View File

@ -327,6 +327,114 @@ p {
object-fit: contain;
}
/* Modal / Popup */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
animation: modalFadeIn 0.3s ease;
}
@keyframes modalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal.show {
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: var(--white);
padding: 40px;
border-radius: var(--radius-lg);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 90%;
text-align: center;
animation: modalSlideIn 0.3s ease;
}
@keyframes modalSlideIn {
from { transform: translateY(-50px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal-header {
position: relative;
margin-bottom: var(--space-4);
}
.modal-header h2 {
color: var(--olive);
font-size: 1.5rem;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
font-size: 28px;
color: var(--black);
background: none;
border: none;
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.modal-body {
color: var(--black);
font-size: 1rem;
line-height: 1.6;
margin-bottom: var(--space-6);
}
.modal-footer {
display: flex;
gap: var(--space-2);
justify-content: center;
}
/* Snackbar */
.snackbar {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: var(--olive);
color: var(--white);
padding: var(--space-3) var(--space-6);
border-radius: var(--radius-pill);
font-size: 1rem;
font-weight: 600;
font-family: var(--font-main);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
opacity: 0;
transition: transform 0.4s ease, opacity 0.4s ease;
z-index: 9999;
pointer-events: none;
}
.snackbar--visible {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
.snackbar--danger {
background: var(--tomato);
}
/* Footer */
.footer {
display: flex;

View File

@ -64,8 +64,13 @@
<section id="event-grid" class="event-list"></section>
</main>
<!-- Page logic: data loading, filtering and card rendering -->
<!-- Seitenlogik: Daten laden, filtern und Event-Karten rendern -->
<script src="js/event_overview.js"></script>
<!-- Snackbar: Feedback bei An-/Abmeldung -->
<div class="snackbar" id="snackbar"></div>
<!-- Instagram Einladung -->
<div class="instagram-invite">
<a href="https://www.instagram.com" target="_blank" rel="noopener noreferrer" class="instagram-invite__link">
<img src="assets/Icon_instagram.png" alt="Instagram" class="instagram-invite__icon" />

View File

@ -9,7 +9,7 @@ document.addEventListener('DOMContentLoaded', () => {
const filterButtons = document.querySelectorAll('.category-item');
const locationFilter = document.getElementById('location-filter');
const dateFilter = document.getElementById('date-filter');
const locationIconPath = 'assets/location-pin.svg';
const locationIconPath = 'assets/icon_location-pin.svg';
// -------------------------------------------------------------
// In-memory state for fetched events and currently active category.
@ -329,7 +329,7 @@ document.addEventListener('DOMContentLoaded', () => {
<div class="empty-state">
<p class="empty-state-kicker">Keine Treffer</p>
<h3>Schade, aktuell gibt es hier keine Events.</h3>
<p>Starte dein eigenes Dinner und bringe die Community an deinen Tisch.</p>
<p>Starte dein eigenes Event und bringe die Community an deinen Tisch.</p>
<a class="empty-state-link" href="event_create.html">
<button class="empty-state-btn" type="button">Event erstellen</button>
</a>
@ -435,12 +435,29 @@ document.addEventListener('DOMContentLoaded', () => {
: [];
const idSet = new Set(currentIds);
// Abmeldung: Benutzer vom Event entfernen und Snackbar anzeigen.
if (action === 'unregister') {
idSet.delete(Number(event.id));
const snackbar = document.getElementById('snackbar');
if (snackbar) {
snackbar.textContent = 'Du wurdest erfolgreich abgemeldet.';
snackbar.classList.add('snackbar--danger', 'snackbar--visible');
setTimeout(() => {
snackbar.classList.remove('snackbar--visible');
setTimeout(() => snackbar.classList.remove('snackbar--danger'), 400);
}, 3000);
}
}
// Anmeldung: Benutzer zum Event hinzufuegen und Snackbar anzeigen.
if (action === 'register' && !isFull && !isRegistrationClosed) {
idSet.add(Number(event.id));
const snackbar = document.getElementById('snackbar');
if (snackbar) {
snackbar.textContent = 'Du wurdest erfolgreich angemeldet.';
snackbar.classList.add('snackbar--visible');
setTimeout(() => snackbar.classList.remove('snackbar--visible'), 3000);
}
}
nextRegistrationMap[currentUser.email] = Array.from(idSet);

View File

@ -148,11 +148,24 @@ document.addEventListener('DOMContentLoaded', () => {
});
logoutButton.addEventListener('click', () => {
localStorage.removeItem(CURRENT_USER_KEY);
window.location.href = 'login.html';
const logoutModal = document.getElementById('logoutModal');
logoutModal.classList.add('show');
document.body.style.overflow = 'hidden';
});
}
// Globale Funktionen fuer das Logout-Modal.
window.closeLogoutModal = function() {
const logoutModal = document.getElementById('logoutModal');
logoutModal.classList.remove('show');
document.body.style.overflow = 'auto';
};
window.confirmLogout = function() {
localStorage.removeItem(CURRENT_USER_KEY);
window.location.href = 'index.html';
};
// Reagiert auf Aktionen in der Liste "Meine Events" per Event Delegation.
function handleHostedListClick(event) {
const target = event.target;
@ -219,6 +232,17 @@ document.addEventListener('DOMContentLoaded', () => {
}
unregisterFromEvent(eventId, currentUser.email);
// Snackbar: Feedback bei erfolgreicher Abmeldung.
const snackbar = document.getElementById('snackbar');
if (snackbar) {
snackbar.textContent = 'Du wurdest erfolgreich abgemeldet.';
snackbar.classList.add('snackbar--danger', 'snackbar--visible');
setTimeout(() => {
snackbar.classList.remove('snackbar--visible');
setTimeout(() => snackbar.classList.remove('snackbar--danger'), 400);
}, 3000);
}
return;
}

View File

@ -31,8 +31,8 @@ document.addEventListener('DOMContentLoaded', () => {
const signupIsActive = currentPage === 'signup.html';
const isIndex = currentPage === 'index.html' || currentPage === '';
// Auf der Startseite nur Login anzeigen.
if (isIndex) {
// Auf der Startseite, Login und Signup nur Login anzeigen.
if (isIndex || loginIsActive || signupIsActive) {
return `
<a
class="button-small"
@ -61,20 +61,23 @@ document.addEventListener('DOMContentLoaded', () => {
>
Signup
</a>
`;`;
`;
}
// Baut die Navigation fuer eingeloggte Benutzer.
function buildLoggedInNavigation() {
function buildLoggedInNavigation(user) {
const initial = (user.vorname || 'U').charAt(0).toUpperCase();
const isEventOverview = currentPage === 'event_overview.html';
return `
<a class="nav-tab" href="event_overview.html">Event finden</a>
<a class="nav-tab" href="event_create.html">Event erstellen</a>
<a class="button-small" href="my_profil.html" aria-label="Mein Profil">Mein Profil</a>
${isEventOverview ? '' : '<a class="nav-tab" href="event_overview.html">Event finden</a>'}
<a class="button-small" href="event_create.html">Event erstellen</a>
<a class="profile-pill" href="my_profil.html" aria-label="Mein Profil" title="${user.vorname || 'Profil'}">${initial}</a>
`;
}
const currentUser = getCurrentUser();
const nextMarkup = currentUser ? buildLoggedInNavigation() : buildLoggedOutNavigation();
const nextMarkup = currentUser ? buildLoggedInNavigation(currentUser) : buildLoggedOutNavigation();
// Wendet das passende Markup auf alle vorhandenen Kopf-Navigationen an.
navContainers.forEach(container => {

View File

@ -46,6 +46,7 @@ function openWelcomeModal() {
function closeWelcomeModal() {
welcomeModal.classList.remove('show');
document.body.style.overflow = 'auto';
window.location.href = 'event_overview.html';
}
// Hauptfunktion fuer Formularvalidierung und Speicherung.
@ -138,10 +139,7 @@ function validateForm(event) {
setCurrentUser(newUser);
openWelcomeModal();
// Hier koennte spaeter ein echter API-Call zum Backend stehen.
// Weiterleitung zur Event-Overview-Seite.
window.location.href = 'event_overview.html';
// Weiterleitung erfolgt beim Klick auf "Weiter zu den Events".
}
}

View File

@ -20,8 +20,7 @@
<img src="assets/logo_invite.svg" alt="Invité">
</a>
<nav class="nav-tab-links" aria-label="Hauptnavigation">
<a class="button-small auth-nav-button auth-nav-button--active" href="login.html" aria-label="Login" aria-current="page">Login</a>
<a class="button-small auth-nav-button auth-nav-button--default" href="signup.html" aria-label="Signup">Signup</a>
<a class="button-small" href="login.html" aria-label="Login">Login</a>
</nav>
</div>
</header>
@ -30,7 +29,7 @@
<div class="main-content">
<div class="container">
<div class="image-section">
<img src="assets/cooking.jpg" alt="Social Cooking">
<img src="assets/index_cooking.jpg" alt="Social Cooking">
</div>
<div class="form-section">

View File

@ -103,6 +103,26 @@
</section>
</main>
<!-- Logout Confirmation Modal -->
<div id="logoutModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<button class="close-btn" onclick="closeLogoutModal()">&times;</button>
<h2>Abmelden?</h2>
</div>
<div class="modal-body">
Bist du sicher, dass du dich abmelden möchtest?
</div>
<div class="modal-footer">
<button class="button button--outline" type="button" onclick="closeLogoutModal()">Abbrechen</button>
<button class="button" type="button" onclick="confirmLogout()">Abmelden</button>
</div>
</div>
</div>
<!-- Snackbar: Feedback bei Abmeldung von Events -->
<div class="snackbar" id="snackbar"></div>
<script src="js/my_profil.js"></script>
</body>
</html>

View File

@ -20,8 +20,7 @@
<img src="assets/logo_invite.svg" alt="Invite Logo">
</a>
<nav class="nav-tab-links" aria-label="Hauptnavigation">
<a class="button-small auth-nav-button auth-nav-button--default" href="login.html" aria-label="Login">Login</a>
<a class="button-small auth-nav-button auth-nav-button--active" href="signup.html" aria-label="Signup" aria-current="page">Signup</a>
<a class="button-small" href="login.html" aria-label="Login">Login</a>
</nav>
</div>
</header>
@ -30,7 +29,7 @@
<div class="main-content">
<div class="container">
<div class="image-section">
<img src="assets/cooking.jpg" alt="Social Cooking">
<img src="assets/index_cooking.jpg" alt="Social Cooking">
</div>
<div class="form-section">
@ -81,13 +80,13 @@
<div class="modal-content">
<div class="modal-header">
<button class="close-btn" onclick="closeWelcomeModal()">&times;</button>
<h2>🎉 Willkommen bei Invité!</h2>
<h2>Konto erfolgreich erstellt!</h2>
</div>
<div class="modal-body">
Hier findest du die Übersicht zu den aktuellsten Events.
Willkommen bei Invité! Dein Account wurde erfolgreich erstellt. Entdecke jetzt die neuesten Events in deiner Nähe.
</div>
<div class="modal-footer">
<button class="btn-primary" onclick="closeWelcomeModal()">Weiter zu den Events</button>
<button class="button" onclick="closeWelcomeModal()">Weiter zu den Events</button>
</div>
</div>
</div>