From ac9fd4cf423b1d15f02d20282a70886f90780bfb Mon Sep 17 00:00:00 2001 From: Javier Blanco Date: Wed, 6 May 2026 20:49:10 +0700 Subject: [PATCH] Fix schedule --- classes/index.html | 4 +- connect/index.html | 4 +- css/style.css | 173 +++++++++++++++++++++--- festivals/index.html | 4 +- index.html | 4 +- js/main.js | 303 ++++++++++++++++++++++++++++++++++++++----- playlists/index.html | 4 +- schedule/index.html | 46 +++---- studios/index.html | 4 +- 9 files changed, 459 insertions(+), 87 deletions(-) diff --git a/classes/index.html b/classes/index.html index 73d396a..697ffc3 100644 --- a/classes/index.html +++ b/classes/index.html @@ -13,7 +13,7 @@ - + @@ -387,7 +387,7 @@ - + \ No newline at end of file diff --git a/connect/index.html b/connect/index.html index 5a58efa..1bd8373 100644 --- a/connect/index.html +++ b/connect/index.html @@ -13,7 +13,7 @@ - + @@ -205,7 +205,7 @@ - + \ No newline at end of file diff --git a/css/style.css b/css/style.css index ff4fe41..113c9f9 100644 --- a/css/style.css +++ b/css/style.css @@ -1253,7 +1253,7 @@ body>*:not(.sfB-bg):not(.sfB-topbar) { .sfB-row { display: grid; - grid-template-columns: 130px 75px 1fr 1fr 1fr 95px; + grid-template-columns: 140px 130px 1fr 1fr 1fr 1fr; align-items: center; gap: 14px; padding: 18px 22px; @@ -1360,14 +1360,20 @@ body>*:not(.sfB-bg):not(.sfB-topbar) { font-weight: 500; } -.sfB-row .col-social .sub { - display: block; - font-family: 'JetBrains Mono', monospace; - font-size: 9px; - letter-spacing: 0.5px; - color: rgba(255, 246, 232, 0.6); - margin-top: 4px; - text-transform: uppercase; +.sfB-row .col-org { + font-size: 15px; + font-weight: 700; + background: linear-gradient(135deg, var(--sf-coral), var(--sf-gold)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.sfB-row .col-org a { + background: linear-gradient(135deg, var(--sf-coral), var(--sf-gold)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; } .sfB-row .col-music { @@ -1398,14 +1404,6 @@ body>*:not(.sfB-bg):not(.sfB-topbar) { color: #7ed4dd; } -.sfB-row .col-org { - font-size: 11px; - color: rgba(255, 246, 232, 0.5); - text-align: right; - letter-spacing: 0.3px; - line-height: 1.4; -} - .sfB-row.today .tonight-label { font-family: 'JetBrains Mono', monospace; font-size: 8px; @@ -2261,6 +2259,147 @@ body>*:not(.sfB-bg):not(.sfB-topbar) { } } +/* ── Schedule: click hint ── */ +.sfB-sched-hint { + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + letter-spacing: 0.5px; + color: rgba(255, 246, 232, 0.75); + text-align: center; + margin: -4px 0 12px; +} + +/* ── Schedule: organizer links inside row ── */ +.sfB-row .col-org a { + text-decoration: none; +} + +.sfB-row .col-org a:hover { + background: none; + -webkit-background-clip: unset; + background-clip: unset; + color: var(--sf-teal); +} + +/* ── Event detail modal ── */ +.sfB-ev-modal { + display: none; + position: fixed; + inset: 0; + z-index: 9000; + align-items: center; + justify-content: center; +} + +.sfB-ev-modal.open { + display: flex; +} + +.sfB-ev-modal-box { + position: relative; + z-index: 1; + background: #1a1a1a; + border: 1px solid rgba(255, 246, 232, 0.12); + border-radius: 20px; + width: min(520px, calc(100vw - 32px)); + max-height: calc(100vh - 64px); + overflow-y: auto; +} + +.sfB-ev-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 22px 24px 18px; + border-bottom: 1px solid rgba(255, 246, 232, 0.08); + position: sticky; + top: 0; + background: #1a1a1a; + z-index: 1; +} + +.sfB-ev-day-time { + font-family: 'JetBrains Mono', monospace; + font-size: 13px; + letter-spacing: 1px; + text-transform: uppercase; + color: var(--sf-coral); + margin-bottom: 6px; +} + +.sfB-ev-name { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 22px; + font-weight: 800; + color: var(--sf-paper); + letter-spacing: -0.5px; + line-height: 1.1; +} + +.sfB-ev-body { + padding: 20px 24px 24px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.sfB-ev-row { + display: grid; + grid-template-columns: 64px 1fr; + gap: 10px; + align-items: flex-start; +} + +.sfB-ev-label { + font-family: 'JetBrains Mono', monospace; + font-size: 9px; + letter-spacing: 1.2px; + text-transform: uppercase; + color: rgba(255, 246, 232, 0.35); + padding-top: 3px; +} + +.sfB-ev-val { + font-size: 13px; + color: rgba(255, 246, 232, 0.85); + display: flex; + flex-direction: column; + gap: 3px; +} + +.sfB-ev-val a { + color: #5dd6df; + text-decoration: none; +} + +.sfB-ev-val a:hover { + color: #a8eef2; +} + +.sfB-ev-address { + font-size: 11px; + color: rgba(255, 246, 232, 0.75); +} + +.sfB-ev-fee { + font-family: 'JetBrains Mono', monospace; + font-size: 15px; + font-weight: 600; + color: var(--sf-gold); +} + +.sfB-ev-chips { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 4px; +} + +.sfB-ev-offer { + color: var(--sf-gold) !important; +} + /* ── Playlists page ── */ .sfB-pl-section { margin-top: 40px; diff --git a/festivals/index.html b/festivals/index.html index d53a5f9..33e81ee 100644 --- a/festivals/index.html +++ b/festivals/index.html @@ -13,7 +13,7 @@ - + @@ -256,7 +256,7 @@ - + \ No newline at end of file diff --git a/index.html b/index.html index 5e2ea14..5c58ee5 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ - + @@ -286,7 +286,7 @@ - + \ No newline at end of file diff --git a/js/main.js b/js/main.js index a07ccca..d8e47b5 100644 --- a/js/main.js +++ b/js/main.js @@ -4,16 +4,126 @@ // ============================================================ const SCHEDULE = [ - { day: 'Monday', when: '8:30 pm', venue: 'Webe Coffee', social: 'BOM - Bachata On Monday', music: '8 Bachata · 1 Salsa · 1 Kizomba', city: 'Da Nang', organizer: 'Luu Phuong & Shai', mapUrl: 'https://maps.app.goo.gl/sznmGXHmiiaWf1ke6' }, - { day: 'Tuesday', when: '9 pm', venue: 'La Riva', social: 'Latino Dancing', music: '2 Salsa · 2 Bachata · 2 Kizomba', city: 'Hoi An', organizer: 'David Tavares', mapUrl: 'https://maps.app.goo.gl/3ZyB8icd8oswRghE7' }, - { day: 'Tuesday', when: '9 pm', venue: 'Caliz Bar', social: 'Sensual Night', music: '3 Bachata · 2 Salsa · 2 Kizomba', city: 'Da Nang', organizer: 'Daisy Nguyen', mapUrl: 'https://maps.app.goo.gl/YagFKw2gcTJp9PJc7' }, - { day: 'Wednesday', when: '7:30 pm', venue: 'Ket Fai Bar', social: 'Bachata Kiz Night', music: 'Kizomba 7:30 · Bachata 9 pm', city: 'Da Nang', organizer: 'Sean Kim', mapUrl: 'https://maps.app.goo.gl/XYxY4UCnnJjCAomK6' }, - { day: 'Thursday', when: '9 pm', venue: 'Malibu', social: 'Salsa Nights', music: '2 Salsa · 2 Bachata', city: 'Da Nang', organizer: 'Lucho Giraldes', mapUrl: 'https://maps.app.goo.gl/hZttmc9UcymnKfWP8' }, - { day: 'Friday', when: '9 pm', venue: 'An Thuong By Night', social: 'Dance Unity Party', music: '2 Salsa · 2 Bachata · 2 Kizomba', city: 'Da Nang', organizer: 'Nadiya Yagfarova', mapUrl: 'https://maps.app.goo.gl/9cHcJcDgan9ntowt9' }, - { day: 'Saturday', when: '9 pm', venue: 'La Riva', social: 'Latino Dancing', music: '2 Salsa · 2 Bachata · 2 Kizomba', city: 'Hoi An', organizer: 'David Tavares', mapUrl: 'https://maps.app.goo.gl/3ZyB8icd8oswRghE7' }, - { day: 'Saturday', when: '9 pm', venue: 'An Thuong By Night', social: 'Bachata Party', music: 'Only Bachata', city: 'Da Nang', organizer: 'Vaclav & Kseniya', mapUrl: 'https://maps.app.goo.gl/9cHcJcDgan9ntowt9' }, - { day: 'Sunday', when: '9 pm', venue: 'Last Call', social: 'Latin Dance Social', music: '3 Bachata · 2 Salsa', city: 'Da Nang', organizer: 'Vaclav & Kseniya', mapUrl: 'https://maps.app.goo.gl/1tEE4itDEeErhEmG6' }, - { day: 'Sunday', when: '9 pm', venue: 'Corner Bar', social: 'Sunday Latin', music: '3 Bachata · 2 Salsa · 3 Kizomba', city: 'Da Nang', organizer: 'Daisy Nguyen', mapUrl: 'https://maps.app.goo.gl/QyuWvCg2DZoFiRyd6' }, + { + day: 'Monday', + name: 'Bachata on Mondays', + when: '8:00 PM – 11:00 PM', + fee: '100k VND', + venue: 'Webe Coffee', + address: '14–16 Nguyễn Hữu Thông, An Hải, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/DiRPZrr4wyW1Wy1eA', + music: '8 Bachata · 1 Salsa · 1 Kizomba', + city: 'Da Nang', + organizer: ['Frog and Goose'], + organizerUrl: ['https://www.instagram.com/fgbachata/'], + offer: '1 drink included · Workshop included', + vibe: 'Workshop (8PM–9:30PM) & Social', + }, + { + day: 'Tuesday', + name: 'SBK', + when: '9:00 PM – 11:30 PM', + fee: '100k VND', + venue: 'Cáliz', + address: '47–49 An Thượng 3, Ngũ Hành Sơn, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/JUBLZ5qHxkwXCe5u7', + music: '2 Salsa · 3 Bachata · 2 Kizomba', + city: 'Da Nang', + organizer: ['Daisy Nguyen'], + organizerUrl: ['https://www.facebook.com/minhtrang610'], + offer: '50k for first 10 ladies', + vibe: 'Elegant Wine Bar', + }, + { + day: 'Wednesday', + name: 'Kiz & Bachata', + when: '7:30 PM – Late', + fee: 'FREE', + venue: 'Kết Fai', + address: '138 Đỗ Bá, Ngũ Hành Sơn, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/W6nRJfwr2Vhy8iK66', + music: 'Kizomba · Bachata', + city: 'Da Nang', + organizer: ['Sean Kim'], + organizerUrl: ['https://www.facebook.com/happynowkkop'], + offer: 'Buy 1 Cocktail, Get 1 Wine', + vibe: 'Kizomba (7:30PM–9:15PM) & Bachata (9:30PM–Late) · Chill bar', + }, + { + day: 'Thursday', + name: 'Salsa & Bachata', + when: '9:00 PM – 11:30 PM', + fee: '100k VND', + venue: 'Malibu Beach Club', + address: 'Bãi tắm, 3 Võ Nguyên Giáp, An Hải, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/GZQC5micBRunbVNP8', + music: '2 Salsa · 2 Bachata', + city: 'Da Nang', + organizer: ['Lucho Giraldes'], + organizerUrl: ['https://www.facebook.com/luchogiraldespersonal'], + offer: 'Beach club snacks available', + vibe: 'Open-air Sea Breeze', + }, + { + day: 'Friday', + name: 'Salsa & Bachata', + when: '8:00 PM – Late', + fee: '100k VND', + venue: 'An Thượng By Night', + address: '100 Lã Xuân Oai, Ngũ Hành Sơn, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/R6mH4uQfAZtUWiH87', + music: '2 Salsa · 2 Bachata · 2 Kizomba', + city: 'Da Nang', + organizer: ['Nadya Yafarova'], + organizerUrl: ['https://www.facebook.com/yagfarova'], + offer: 'Free for first 10 ladies', + vibe: 'Community Family', + }, + { + day: 'Saturday', + name: '100% Kizomba', + when: '8:00 PM – 11:00 PM', + fee: '100k VND', + venue: 'LiiDy Studio', + address: '75 Thái Phiên, Hải Châu, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/GZ5UtDPFafW1BCSP9', + music: 'Kizomba', + city: 'Da Nang', + organizer: ['Kate Bvote'], + organizerUrl: ['https://www.facebook.com/kate.bovt'], + offer: '1 Drinking water included', + vibe: 'Deep Connection', + }, + { + day: 'Sunday', + name: 'SBK', + when: '8:30 PM – Late', + fee: '50k VND', + venue: 'The Workshop', + address: '35 Chế Lan Viên, Ngũ Hành Sơn, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/xxVwTocSQHKU1Ejd8', + music: '2 Salsa · 3 Bachata · 3 Kizomba', + city: 'Da Nang', + organizer: ['Daisy Nguyen'], + organizerUrl: ['https://www.facebook.com/minhtrang610'], + offer: '1-hr Kiz session included', + vibe: 'Festival style rotation', + }, + { + day: 'Sunday', + name: 'Salsa & Bachata', + when: '8:00 PM – 11:30 PM', + fee: '100k VND', + venue: 'An Thượng By Night', + address: '100 Lã Xuân Oai, Ngũ Hành Sơn, Đà Nẵng', + mapUrl: 'https://maps.app.goo.gl/R6mH4uQfAZtUWiH87', + music: '2 Salsa · 2 Bachata', + city: 'Da Nang', + organizer: ['Vaclav', 'Ksenia Tokareva'], + organizerUrl: ['https://www.facebook.com/vasek.nemec.1', 'https://www.facebook.com/omks234'], + offer: '1 drink included & 10% discount on meals', + vibe: 'Workshop (8PM–9PM) & Social', + }, ]; const DAYS_ORDER = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; @@ -34,6 +144,15 @@ function musicToChips(music) { }).join(''); } +// Build organizer name(s) as linked text +function organizerLinks(organizer, organizerUrl, stopProp) { + const stop = stopProp ? ' onclick="event.stopPropagation()"' : ''; + return organizer.map((name, i) => { + const url = organizerUrl && organizerUrl[i]; + return url ? `${name}` : name; + }).join(' & '); +} + // ---- Nav (mobile) ---- function initNav() { const hamburger = document.getElementById('sfB-hamburger'); @@ -49,12 +168,123 @@ function initNav() { link.addEventListener('click', () => mobileNav.classList.remove('open')); }); - // close on backdrop click mobileNav.addEventListener('click', e => { if (e.target === mobileNav) mobileNav.classList.remove('open'); }); } +// ---- Event detail modal ---- +let _sortedSchedule = []; + +function openEventModal(row) { + const modal = document.getElementById('sfB-ev-modal'); + if (!modal) return; + + const now = new Date(); + const offset = (DAYS_ORDER.indexOf(row.day) - now.getDay() + 7) % 7; + const date = new Date(now); + date.setDate(now.getDate() + offset); + const dateLabel = `${date.getDate()}/${date.getMonth() + 1}`; + + document.getElementById('sfB-ev-modal-day').textContent = `${dateLabel} · ${row.day} · ${row.when}`; + document.getElementById('sfB-ev-modal-name').textContent = row.name; + + const orgLinks = organizerLinks(row.organizer, row.organizerUrl, false); + + document.getElementById('sfB-ev-modal-body').innerHTML = ` +
+ Venue + + 📍 ${row.venue} + ${row.address} + +
+
+ Music + ${musicToChips(row.music)} +
+
+ Host + ${orgLinks} +
+
+ Entry + ${row.fee} +
+
+ Offer + ${row.offer} +
+
+ Vibe + ${row.vibe} +
+ `; + + modal.classList.add('open'); + document.body.style.overflow = 'hidden'; +} + +function initEventModal() { + const modal = document.getElementById('sfB-ev-modal'); + if (!modal) return; + + function closeModal() { + modal.classList.remove('open'); + document.body.style.overflow = ''; + } + + document.getElementById('sfB-ev-modal-close').addEventListener('click', closeModal); + document.getElementById('sfB-ev-modal-overlay').addEventListener('click', closeModal); + document.addEventListener('keydown', e => { + if (e.key === 'Escape' && modal.classList.contains('open')) closeModal(); + }); +} + +// ---- Schedule: dynamic filter pills ---- +function renderScheduleFilters(events) { + const container = document.getElementById('sfB-schedule-pills'); + if (!container) return; + + const styleOptions = [ + { key: 'salsa', label: 'Salsa' }, + { key: 'bachata', label: 'Bachata' }, + { key: 'kizomba', label: 'Kizomba' }, + { key: 'zouk', label: 'Zouk' }, + ]; + const cityOptions = [ + { key: 'hoian', label: 'Hoi An', match: 'hoi an' }, + ]; + + const activeStyles = styleOptions.filter(s => + events.some(e => e.music.toLowerCase().includes(s.key)) + ); + const activeCities = cityOptions.filter(c => + events.some(e => e.city.toLowerCase().includes(c.match)) + ); + + container.innerHTML = [ + '', + ...activeStyles.map(s => ``), + ...activeCities.map(c => ``), + ].join(''); + + container.querySelectorAll('.filter-pill').forEach(pill => { + pill.addEventListener('click', () => { + container.querySelectorAll('.filter-pill').forEach(p => p.classList.remove('active')); + pill.classList.add('active'); + const f = pill.dataset.filter; + document.querySelectorAll('#sfB-schedule-list .sfB-row').forEach(row => { + if (f === 'all') { row.style.display = ''; return; } + const music = row.dataset.music || ''; + const city = row.dataset.city || ''; + const matchCity = f === 'danang' ? 'da nang' : f === 'hoian' ? 'hoi an' : f; + row.style.display = (music.includes(f) || city.includes(matchCity)) ? '' : 'none'; + }); + }); + }); +} + // ---- Schedule: full list (schedule.html) ---- function renderScheduleList() { const list = document.getElementById('sfB-schedule-list'); @@ -64,28 +294,29 @@ function renderScheduleList() { const todayName = DAYS_ORDER[now.getDay()]; const startIdx = DAYS_ORDER.indexOf(todayName); - // Map each day name to how many days from today it is (0–6) const dayOffset = {}; DAYS_ORDER.forEach(name => { dayOffset[name] = (DAYS_ORDER.indexOf(name) - startIdx + 7) % 7; }); - const sorted = [...SCHEDULE].sort((a, b) => dayOffset[a.day] - dayOffset[b.day]); + _sortedSchedule = [...SCHEDULE].sort((a, b) => dayOffset[a.day] - dayOffset[b.day]); - list.innerHTML = sorted.map(row => { + renderScheduleFilters(_sortedSchedule); + + list.innerHTML = _sortedSchedule.map((row, i) => { const isToday = row.day === todayName; const offset = dayOffset[row.day]; const date = new Date(now); date.setDate(now.getDate() + offset); const dayNum = `${date.getDate()}/${date.getMonth() + 1}`; - const cityClass = row.city === 'Hoi An' ? 'hoian' : 'danang'; - const venueEl = row.mapUrl - ? `${row.venue}` - : row.venue; + const orgHtml = organizerLinks(row.organizer, row.organizerUrl, true); return ` -
+
${dayNum} @@ -93,19 +324,27 @@ function renderScheduleList() {
${isToday ? '▶ Tonight' : ''}
-
${row.when || ''}
- +
${row.when}
+ +
${orgHtml}
${musicToChips(row.music)}
📍 - ${venueEl} + ${row.venue}
-
${row.city}
`; }).join(''); + + list.querySelectorAll('.sfB-row').forEach(el => { + function openRow() { + const idx = parseInt(el.dataset.idx, 10); + openEventModal(_sortedSchedule[idx]); + } + el.addEventListener('click', openRow); + el.addEventListener('keydown', e => { + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openRow(); } + }); + }); } // ---- Home: "Coming up" teaser (4 cards starting from today) ---- @@ -132,12 +371,13 @@ function renderScheduleTeaser() { const date = new Date(now); date.setDate(now.getDate() + offset); const dayNum = `${date.getDate()}/${date.getMonth() + 1}`; + const orgText = row.organizer.join(' & '); return `
${dayNum} ${row.day}
${row.when || ''}
-

${row.social}

-
${row.organizer}
+

${row.name}

+
${orgText}
@ ${row.venue} · ${row.city}
${musicToChips(row.music)}
@@ -147,6 +387,9 @@ function renderScheduleTeaser() { // ---- Filter pills (studios.html / classes.html) ---- function initFilters() { + const cards = document.querySelectorAll('.filterable-card'); + if (!cards.length) return; + const pills = document.querySelectorAll('.filter-pill'); if (!pills.length) return; @@ -156,7 +399,7 @@ function initFilters() { pill.classList.add('active'); const filter = pill.dataset.filter; - document.querySelectorAll('.filterable-card').forEach(card => { + cards.forEach(card => { if (filter === 'all') { card.classList.remove('hidden'); } else { @@ -176,7 +419,6 @@ function lockPalmSizes() { const vh = window.innerHeight; const clamp = (val, min, max) => Math.min(Math.max(val, min), max); - // Each entry: [size, { top|bottom, left|right } in px] const config = { 'corner-tl': { size: clamp(vw * 0.40, 100, 620), pos: { top: vh * -0.08, left: vw * -0.15 } }, 'corner-tr': { size: clamp(vw * 0.40, 100, 620), pos: { top: vh * -0.08, right: vw * -0.10 } }, @@ -222,6 +464,7 @@ document.addEventListener('DOMContentLoaded', () => { renderScheduleList(); renderScheduleTeaser(); initFilters(); + initEventModal(); const yearEl = document.getElementById('footer-year'); if (yearEl) yearEl.textContent = new Date().getFullYear(); lockPalmSizes(); diff --git a/playlists/index.html b/playlists/index.html index 5e2c3c4..dd36472 100644 --- a/playlists/index.html +++ b/playlists/index.html @@ -13,7 +13,7 @@ - + @@ -594,7 +594,7 @@
- + + diff --git a/studios/index.html b/studios/index.html index b7fcff1..8aa47a7 100644 --- a/studios/index.html +++ b/studios/index.html @@ -13,7 +13,7 @@ - + @@ -249,7 +249,7 @@ - + \ No newline at end of file