Update event dates and participant handling; adjust visibility window to 24 hours
This commit is contained in:
parent
fa8a7f1fc2
commit
e3ac1a11f0
@ -4,11 +4,11 @@
|
|||||||
"title": "Italienische Tavolata",
|
"title": "Italienische Tavolata",
|
||||||
"location": "Luzern",
|
"location": "Luzern",
|
||||||
"address": "Pilatusstrasse 18, 6003 Luzern",
|
"address": "Pilatusstrasse 18, 6003 Luzern",
|
||||||
"date": "11. APR. 2026",
|
"date": "22. APR. 2026",
|
||||||
"time": "15:30 UHR",
|
"time": "15:30 UHR",
|
||||||
"category": "Dinner",
|
"category": "Dinner",
|
||||||
"diet": "Vegetarisch",
|
"diet": "Vegetarisch",
|
||||||
"spots": 6,
|
"spots": 8,
|
||||||
"host": {
|
"host": {
|
||||||
"name": "Ferdinando",
|
"name": "Ferdinando",
|
||||||
"initial": "F"
|
"initial": "F"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"title": "Noche Peruana",
|
"title": "Noche Peruana",
|
||||||
"location": "Chur",
|
"location": "Chur",
|
||||||
"address": "Obere Gasse 41, 7000 Chur",
|
"address": "Obere Gasse 41, 7000 Chur",
|
||||||
"date": "16. Juni 2026",
|
"date": "12. April 2026",
|
||||||
"time": "19:00 UHR",
|
"time": "19:00 UHR",
|
||||||
"category": "Dinner",
|
"category": "Dinner",
|
||||||
"diet": "Omnivore",
|
"diet": "Omnivore",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
const USERS_STORAGE_KEY = 'socialCookingUsers';
|
||||||
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// DOM entry point and shared asset path.
|
// DOM entry point and shared asset path.
|
||||||
@ -50,6 +51,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStoredUsers() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(USERS_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Benutzerdaten konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserDisplayName(user) {
|
||||||
|
if (!user) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstName = String(user.vorname || '').trim();
|
||||||
|
const lastName = String(user.nachname || '').trim();
|
||||||
|
|
||||||
|
return (firstName || `${firstName} ${lastName}`.trim() || String(user.email || '').trim()).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResolvedParticipants(event, registrationMap) {
|
||||||
|
const baseParticipants = Array.isArray(event.participants)
|
||||||
|
? event.participants.map(name => String(name || '').trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
const usersByEmail = new Map(
|
||||||
|
getStoredUsers().map(user => [String(user.email || '').trim().toLowerCase(), user])
|
||||||
|
);
|
||||||
|
const participantLookup = new Set(baseParticipants.map(name => name.toLowerCase()));
|
||||||
|
|
||||||
|
Object.entries(registrationMap || {}).forEach(([email, ids]) => {
|
||||||
|
const isRegisteredForEvent = Array.isArray(ids)
|
||||||
|
&& ids.map(id => Number(id)).includes(Number(event.id));
|
||||||
|
|
||||||
|
if (!isRegisteredForEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = usersByEmail.get(String(email || '').trim().toLowerCase());
|
||||||
|
const displayName = getUserDisplayName(user) || String(email || '').trim();
|
||||||
|
const normalizedName = displayName.toLowerCase();
|
||||||
|
|
||||||
|
if (displayName && !participantLookup.has(normalizedName)) {
|
||||||
|
baseParticipants.push(displayName);
|
||||||
|
participantLookup.add(normalizedName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return baseParticipants;
|
||||||
|
}
|
||||||
|
|
||||||
function setRegistrationMap(registrationMap) {
|
function setRegistrationMap(registrationMap) {
|
||||||
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
||||||
}
|
}
|
||||||
@ -114,7 +166,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const msUntilStart = eventDateTime.getTime() - Date.now();
|
const msUntilStart = eventDateTime.getTime() - Date.now();
|
||||||
const twentyfourHoursInMs = 12 * 60 * 60 * 1000;
|
const twentyfourHoursInMs = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
return msUntilStart <= twentyfourHoursInMs;
|
return msUntilStart <= twentyfourHoursInMs;
|
||||||
}
|
}
|
||||||
@ -138,7 +190,7 @@
|
|||||||
return { daysLeft, isClosed: false };
|
return { daysLeft, isClosed: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adresse ist nur im 12h-Fenster VOR Eventstart sichtbar.
|
// Adresse ist nur im 24h-Fenster VOR Eventstart sichtbar.
|
||||||
function isAddressVisibleWindow(event) {
|
function isAddressVisibleWindow(event) {
|
||||||
const eventDateTime = parseEventDateTime(event);
|
const eventDateTime = parseEventDateTime(event);
|
||||||
if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) {
|
if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) {
|
||||||
@ -146,7 +198,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const msUntilStart = eventDateTime.getTime() - Date.now();
|
const msUntilStart = eventDateTime.getTime() - Date.now();
|
||||||
const twentyfourHoursInMs = 12 * 60 * 60 * 1000;
|
const twentyfourHoursInMs = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
return msUntilStart >= 0 && msUntilStart <= twentyfourHoursInMs;
|
return msUntilStart >= 0 && msUntilStart <= twentyfourHoursInMs;
|
||||||
}
|
}
|
||||||
@ -283,16 +335,26 @@
|
|||||||
const specifications = Array.isArray(event.specifications) && event.specifications.length > 0
|
const specifications = Array.isArray(event.specifications) && event.specifications.length > 0
|
||||||
? event.specifications
|
? event.specifications
|
||||||
: [];
|
: [];
|
||||||
const participants = Array.isArray(event.participants) ? event.participants : [];
|
|
||||||
const galleryImages = Array.isArray(event.gallery) && event.gallery.length > 0
|
|
||||||
? event.gallery
|
|
||||||
: [event.image, event.image, event.image];
|
|
||||||
const visibleParticipants = participants.slice(0, 6);
|
|
||||||
const registrationMap = getRegistrationMap();
|
const registrationMap = getRegistrationMap();
|
||||||
const extraRegistrations = countRegistrationsForEvent(registrationMap, event.id);
|
const participants = getResolvedParticipants(event, registrationMap);
|
||||||
const remainingParticipants = Math.max(0, participants.length + extraRegistrations - visibleParticipants.length);
|
const galleryImages = Array.isArray(event.gallery)
|
||||||
|
? event.gallery.filter(Boolean)
|
||||||
|
: [];
|
||||||
|
const galleryMarkup = galleryImages.length > 0
|
||||||
|
? `
|
||||||
|
<div class="detail-gallery detail-gallery-large">
|
||||||
|
${galleryImages.slice(0, 9).map((img, index) => `
|
||||||
|
<button class="detail-gallery-item" type="button" aria-label="Bild ${index + 1} gross anzeigen" data-fullsrc="${img}">
|
||||||
|
<img src="${img}" alt="${event.title} Bild ${index + 1}" class="detail-gallery-image">
|
||||||
|
</button>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
const visibleParticipants = participants.slice(0, 6);
|
||||||
|
const remainingParticipants = Math.max(0, participants.length - visibleParticipants.length);
|
||||||
const totalGuests = Number.isFinite(event.spots) ? event.spots : 0;
|
const totalGuests = Number.isFinite(event.spots) ? event.spots : 0;
|
||||||
const confirmedGuests = participants.length + extraRegistrations;
|
const confirmedGuests = participants.length;
|
||||||
const freePlaces = Math.max(0, totalGuests - confirmedGuests);
|
const freePlaces = Math.max(0, totalGuests - confirmedGuests);
|
||||||
const isFull = freePlaces === 0;
|
const isFull = freePlaces === 0;
|
||||||
const isRegistrationClosed = isRegistrationClosedForEvent(event);
|
const isRegistrationClosed = isRegistrationClosedForEvent(event);
|
||||||
@ -331,7 +393,12 @@
|
|||||||
<p>${event.address}</p>
|
<p>${event.address}</p>
|
||||||
</article>
|
</article>
|
||||||
`
|
`
|
||||||
: '';
|
: `
|
||||||
|
<article class="detail-panel">
|
||||||
|
<h2 class="detail-section-title">Adresse</h2>
|
||||||
|
<p>Vielen Dank für die Anmeldung! Die Adresse für diesen Event wird 24 Stunden vorher genau hier sichtbar sein.</p>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
const detailChips = [
|
const detailChips = [
|
||||||
`<span class="event-tag">${eventCategory}</span>`,
|
`<span class="event-tag">${eventCategory}</span>`,
|
||||||
...event.diet.split(', ').filter(d => d.trim() && d !== 'Keine Angabe').map(d => `<span class="event-tag">${getDietLabel(d.trim())}</span>`),
|
...event.diet.split(', ').filter(d => d.trim() && d !== 'Keine Angabe').map(d => `<span class="event-tag">${getDietLabel(d.trim())}</span>`),
|
||||||
@ -401,13 +468,7 @@
|
|||||||
${addressPanelMarkup}
|
${addressPanelMarkup}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-gallery detail-gallery-large">
|
${galleryMarkup}
|
||||||
${galleryImages.slice(0, 9).map((img, index) => `
|
|
||||||
<button class="detail-gallery-item" type="button" aria-label="Bild ${index + 1} gross anzeigen" data-fullsrc="${img}">
|
|
||||||
<img src="${img}" alt="${event.title} Bild ${index + 1}" class="detail-gallery-image">
|
|
||||||
</button>
|
|
||||||
`).join('')}
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="detail-action-bar">
|
<section class="detail-action-bar">
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
const USERS_STORAGE_KEY = 'socialCookingUsers';
|
||||||
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
||||||
const INFO_MODAL_SHOWN_KEY = 'infoModalShownOnFirstLogin';
|
const INFO_MODAL_SHOWN_KEY = 'infoModalShownOnFirstLogin';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@ -73,6 +74,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStoredUsers() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(USERS_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Benutzerdaten konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserDisplayName(user) {
|
||||||
|
if (!user) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstName = String(user.vorname || '').trim();
|
||||||
|
const lastName = String(user.nachname || '').trim();
|
||||||
|
|
||||||
|
return (firstName || `${firstName} ${lastName}`.trim() || String(user.email || '').trim()).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResolvedParticipants(event, registrationMap) {
|
||||||
|
const baseParticipants = Array.isArray(event.participants)
|
||||||
|
? event.participants.map(name => String(name || '').trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
const usersByEmail = new Map(
|
||||||
|
getStoredUsers().map(user => [String(user.email || '').trim().toLowerCase(), user])
|
||||||
|
);
|
||||||
|
const participantLookup = new Set(baseParticipants.map(name => name.toLowerCase()));
|
||||||
|
|
||||||
|
Object.entries(registrationMap || {}).forEach(([email, ids]) => {
|
||||||
|
const isRegisteredForEvent = Array.isArray(ids)
|
||||||
|
&& ids.map(id => Number(id)).includes(Number(event.id));
|
||||||
|
|
||||||
|
if (!isRegisteredForEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = usersByEmail.get(String(email || '').trim().toLowerCase());
|
||||||
|
const displayName = getUserDisplayName(user) || String(email || '').trim();
|
||||||
|
const normalizedName = displayName.toLowerCase();
|
||||||
|
|
||||||
|
if (displayName && !participantLookup.has(normalizedName)) {
|
||||||
|
baseParticipants.push(displayName);
|
||||||
|
participantLookup.add(normalizedName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return baseParticipants;
|
||||||
|
}
|
||||||
|
|
||||||
function setRegistrationMap(registrationMap) {
|
function setRegistrationMap(registrationMap) {
|
||||||
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
||||||
}
|
}
|
||||||
@ -392,10 +444,9 @@
|
|||||||
const displayTime = formatEventTime(event.time);
|
const displayTime = formatEventTime(event.time);
|
||||||
|
|
||||||
// Capacity logic:
|
// Capacity logic:
|
||||||
// spots = total capacity, participants.length = booked seats.
|
// spots = total capacity, resolved participants = booked seats.
|
||||||
const baseParticipants = Array.isArray(event.participants) ? event.participants.length : 0;
|
const resolvedParticipants = getResolvedParticipants(event, registrationMap);
|
||||||
const extraRegistrations = countRegistrationsForEvent(registrationMap, event.id);
|
const bookedSeats = resolvedParticipants.length;
|
||||||
const bookedSeats = baseParticipants + extraRegistrations;
|
|
||||||
const totalCapacity = event.spots;
|
const totalCapacity = event.spots;
|
||||||
const freePlaces = Math.max(0, totalCapacity - bookedSeats);
|
const freePlaces = Math.max(0, totalCapacity - bookedSeats);
|
||||||
const isFull = freePlaces === 0;
|
const isFull = freePlaces === 0;
|
||||||
|
|||||||
@ -538,7 +538,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gibt true zurück, wenn ein Event innerhalb der nächsten 12 Stunden startet.
|
// Gibt true zurück, wenn ein Event innerhalb der nächsten 24 Stunden startet.
|
||||||
function isAddressVisibleWindow(event) {
|
function isAddressVisibleWindow(event) {
|
||||||
const eventDateTime = parseEventDateTime(event);
|
const eventDateTime = parseEventDateTime(event);
|
||||||
if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) {
|
if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) {
|
||||||
@ -546,7 +546,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const msUntilStart = eventDateTime.getTime() - Date.now();
|
const msUntilStart = eventDateTime.getTime() - Date.now();
|
||||||
const twentyfourHoursInMs = 12 * 60 * 60 * 1000;
|
const twentyfourHoursInMs = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
return msUntilStart >= 0 && msUntilStart <= twentyfourHoursInMs;
|
return msUntilStart >= 0 && msUntilStart <= twentyfourHoursInMs;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user