From eb12dd880de5e02c2129aa8d98300d1d30a8fe42 Mon Sep 17 00:00:00 2001 From: bisco Date: Wed, 25 Mar 2026 19:37:04 +0100 Subject: [PATCH] Refine landing layout: calendar actions and editorial gift section --- index.html | 82 ++++++++++++---- script.js | 138 ++++++++++++++++++++++++++ style.css | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 484 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index fb955ee..8696566 100644 --- a/index.html +++ b/index.html @@ -17,23 +17,8 @@
-

Hey, ci sono i 40 anni del Gianca!

-
-
Save the date
- -
@@ -52,9 +37,26 @@
-
+
Quando Sabato 25 aprile 2026 +
+ + Google Calendar + + +
Dove @@ -69,6 +71,52 @@
+
+
+
+

Dettagli regalo

+
+

Regalo / quota viaggio

+

Un piccolo gesto, con il sorriso.

+

+ Se ti fa piacere contribuire al regalo o alla quota viaggio, qui trovi i riferimenti. +

+

Nota facoltativa: un pensiero semplice è più che sufficiente.

+
+ +
+ + +
+
+
Intestatario
+
Giancarlo ...
+
+
+
IBAN
+
IT00X0000000000000000000000
+
+ + +
+
+
+
Causale consigliata
+
Regalo 40 anni Gianca
+
+
+
+
+
+
+
@@ -175,7 +223,7 @@
diff --git a/script.js b/script.js index c55c6a2..c7534ed 100644 --- a/script.js +++ b/script.js @@ -5,6 +5,33 @@ const attendanceInputs = Array.from(document.querySelectorAll('input[name="atten const successModal = document.getElementById('success-modal'); const successOkButton = document.getElementById('success-ok'); const closeModalNodes = Array.from(document.querySelectorAll('[data-close-modal]')); +const googleCalendarLink = document.getElementById('google-calendar-link'); +const iCalDownloadButton = document.getElementById('ical-download'); +const copyIbanButton = document.getElementById('copy-iban'); +const ibanFeedback = document.getElementById('iban-feedback'); +const giftHolder = document.getElementById('gift-holder'); +const giftIban = document.getElementById('gift-iban'); +const giftCausal = document.getElementById('gift-causal'); +const giftNote = document.getElementById('gift-note'); + +// Modifica qui i dati del calendario evento. +const EVENT_CALENDAR = { + title: 'I 40 anni del Gianca', + location: 'Lost Paradise Beach Club, Bacoli', + description: 'Conferma la tua presenza per i 40 anni del Gianca.', + date: '2026-04-25', + startTime: '12:00', + durationHours: 6, + timezone: 'Europe/Rome' +}; + +// Modifica qui i dati regalo / quota viaggio. +const GIFT_DETAILS = { + holder: 'Giancarlo ...', + iban: 'IT00X0000000000000000000000', + causal: 'Regalo 40 anni Gianca', + note: 'Nota facoltativa: un pensiero semplice e sempre gradito.' +}; function updateAttendanceFields() { const selected = document.querySelector('input[name="attendance"]:checked'); @@ -34,15 +61,126 @@ function closeSuccessModal() { document.body.classList.remove('modal-open'); } +function pad(value) { + return String(value).padStart(2, '0'); +} + +function createFloatingDateTime(date, time, plusHours = 0) { + const [year, month, day] = date.split('-').map(Number); + const [hours, minutes] = time.split(':').map(Number); + const normalizedDate = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0)); + normalizedDate.setUTCHours(normalizedDate.getUTCHours() + plusHours); + + return `${normalizedDate.getUTCFullYear()}${pad(normalizedDate.getUTCMonth() + 1)}${pad(normalizedDate.getUTCDate())}T${pad(normalizedDate.getUTCHours())}${pad(normalizedDate.getUTCMinutes())}00`; +} + +function buildGoogleCalendarUrl(eventData) { + const start = createFloatingDateTime(eventData.date, eventData.startTime, 0); + const end = createFloatingDateTime(eventData.date, eventData.startTime, eventData.durationHours); + const params = new URLSearchParams({ + action: 'TEMPLATE', + text: eventData.title, + details: eventData.description, + location: eventData.location, + ctz: eventData.timezone, + dates: `${start}/${end}` + }); + return `https://calendar.google.com/calendar/render?${params.toString()}`; +} + +function escapeIcsText(value) { + return value + .replace(/\\/g, '\\\\') + .replace(/\n/g, '\\n') + .replace(/,/g, '\\,') + .replace(/;/g, '\\;'); +} + +function buildIcsContent(eventData) { + const start = createFloatingDateTime(eventData.date, eventData.startTime, 0); + const end = createFloatingDateTime(eventData.date, eventData.startTime, eventData.durationHours); + const stamp = `${createFloatingDateTime(eventData.date, eventData.startTime, 0)}Z`; + const uid = `gianca-40-${start}@gianca-event`; + + return [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Gianca40//Event Landing//IT', + 'CALSCALE:GREGORIAN', + 'BEGIN:VEVENT', + `UID:${uid}`, + `DTSTAMP:${stamp}`, + `DTSTART;TZID=${eventData.timezone}:${start}`, + `DTEND;TZID=${eventData.timezone}:${end}`, + `SUMMARY:${escapeIcsText(eventData.title)}`, + `LOCATION:${escapeIcsText(eventData.location)}`, + `DESCRIPTION:${escapeIcsText(eventData.description)}`, + 'END:VEVENT', + 'END:VCALENDAR' + ].join('\r\n'); +} + +function triggerIcsDownload() { + const content = buildIcsContent(EVENT_CALENDAR); + const blob = new Blob([content], { type: 'text/calendar;charset=utf-8' }); + const downloadUrl = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = downloadUrl; + link.download = 'i-40-anni-del-gianca.ics'; + document.body.append(link); + link.click(); + link.remove(); + URL.revokeObjectURL(downloadUrl); +} + +function setIbanFeedback(message) { + if (!ibanFeedback) return; + ibanFeedback.textContent = message; +} + +async function copyIbanToClipboard() { + if (!copyIbanButton) return; + const ibanNodeId = copyIbanButton.dataset.copyTarget; + const ibanText = document.getElementById(ibanNodeId || '')?.textContent?.trim(); + if (!ibanText) return; + + try { + await navigator.clipboard.writeText(ibanText); + setIbanFeedback('IBAN copiato negli appunti.'); + } catch (error) { + const fallbackField = document.createElement('input'); + fallbackField.value = ibanText; + document.body.append(fallbackField); + fallbackField.select(); + const copied = document.execCommand('copy'); + fallbackField.remove(); + setIbanFeedback(copied ? 'IBAN copiato negli appunti.' : 'Copia non riuscita. Riprova.'); + } +} + attendanceInputs.forEach((input) => input.addEventListener('change', updateAttendanceFields)); closeModalNodes.forEach((node) => node.addEventListener('click', closeSuccessModal)); successOkButton?.addEventListener('click', closeSuccessModal); +iCalDownloadButton?.addEventListener('click', triggerIcsDownload); +copyIbanButton?.addEventListener('click', async () => { + await copyIbanToClipboard(); + window.clearTimeout(copyIbanButton.dataset.feedbackTimeoutId); + const timeoutId = window.setTimeout(() => setIbanFeedback(''), 2500); + copyIbanButton.dataset.feedbackTimeoutId = String(timeoutId); +}); document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && successModal?.classList.contains('success-modal--open')) { closeSuccessModal(); } }); updateAttendanceFields(); +if (googleCalendarLink) { + googleCalendarLink.href = buildGoogleCalendarUrl(EVENT_CALENDAR); +} +if (giftHolder) giftHolder.textContent = GIFT_DETAILS.holder; +if (giftIban) giftIban.textContent = GIFT_DETAILS.iban; +if (giftCausal) giftCausal.textContent = GIFT_DETAILS.causal; +if (giftNote) giftNote.textContent = GIFT_DETAILS.note; function encode(data) { return new URLSearchParams(data).toString(); diff --git a/style.css b/style.css index 95f7fe1..7631e93 100644 --- a/style.css +++ b/style.css @@ -286,6 +286,11 @@ img { border: 1px solid rgba(40, 89, 138, 0.08); } +.details-card__item--calendar { + display: grid; + gap: 0.7rem; +} + .details-card__item strong, .details-card__item span { display: block; @@ -300,6 +305,42 @@ img { text-transform: uppercase; } +.calendar-tools { + margin-top: 0.3rem; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.calendar-tools__action { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 40px; + width: auto; + border-radius: 999px; + border: 1px solid rgba(30, 87, 138, 0.28); + background: rgba(255, 255, 255, 0.86); + color: var(--sea-dark); + font-size: 0.88rem; + font-weight: 700; + padding: 0.42rem 0.82rem; + text-decoration: none; + cursor: pointer; + transition: background .18s ease, border-color .18s ease, transform .18s ease, box-shadow .18s ease; +} + +.calendar-tools__action:hover { + background: rgba(30, 87, 138, 0.1); + border-color: rgba(30, 87, 138, 0.5); + transform: translateY(-1px); + box-shadow: 0 8px 16px rgba(21, 60, 96, 0.1); +} + +.calendar-tools__action--button { + font: inherit; +} + .form-card { padding: 1.8rem; } @@ -419,6 +460,205 @@ textarea:focus { cursor: wait; } +.button--secondary { + color: var(--sea-dark); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(241, 246, 252, 0.92) 100%); + border: 1px solid rgba(30, 87, 138, 0.3); + box-shadow: 0 10px 20px rgba(21, 60, 96, 0.12); +} + +.button--compact { + min-height: 44px; + padding: 0.55rem 1rem; + font-size: 0.93rem; +} + +.gift { + padding-top: 1.3rem; +} + +.gift__shell { + display: block; +} + +.gift__title { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.gift__layout { + position: relative; + display: grid; + grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); + column-gap: clamp(1.4rem, 3.2vw, 2.7rem); + row-gap: 0; + align-items: start; +} + +.gift__layout::before { + content: ""; + position: absolute; + left: 0; + top: -0.68rem; + width: min(360px, 44%); + height: 1px; + border-radius: 999px; + background: linear-gradient(90deg, rgba(30, 87, 138, 0.38), rgba(30, 87, 138, 0.05)); +} + +.gift__layout::after { + content: ""; + position: absolute; + right: 0; + bottom: -0.4rem; + width: min(190px, 26%); + height: 1px; + border-radius: 999px; + background: linear-gradient(90deg, rgba(30, 87, 138, 0.04), rgba(30, 87, 138, 0.22)); +} + +.gift__col { + min-width: 0; +} + +.gift__col--left { + display: grid; + gap: 0.55rem; + align-content: start; +} + +.gift__kicker { + margin: 0 0 0.2rem; +} + +.gift__heading { + max-width: 17ch; + margin: 0; + font-size: clamp(1.72rem, 3.25vw, 2.55rem); + line-height: 1.06; + letter-spacing: -0.02em; +} + +.gift__lead { + margin: 0; + color: var(--ink); + max-width: 56ch; + font-size: 0.99rem; + line-height: 1.68; +} + +.gift__note { + margin: 0; + color: var(--ink-soft); + line-height: 1.58; + font-size: 0.92rem; + max-width: 44ch; +} + +.gift__col--right { + position: relative; + align-self: start; + padding-left: clamp(1rem, 1.9vw, 1.3rem); +} + +.gift__accent { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 2px; + border-radius: 999px; + background: linear-gradient(180deg, rgba(30, 87, 138, 0.62), rgba(30, 87, 138, 0.16)); +} + +.gift__accent::after { + content: ""; + position: absolute; + left: 50%; + bottom: -3px; + width: 5px; + height: 5px; + border-radius: 50%; + transform: translateX(-50%); + background: rgba(30, 87, 138, 0.24); +} + +.gift__details { + margin: 0; + display: grid; + gap: 0.74rem; +} + +.gift__row { + margin: 0; + padding: 0 0 0.64rem; + border-bottom: 1px solid rgba(30, 87, 138, 0.18); +} + +.gift__row:last-child { + border-bottom: 0; + padding-bottom: 0.1rem; +} + +.gift__row dt { + color: var(--sea); + font-size: 0.78rem; + font-weight: 800; + letter-spacing: 0.16em; + text-transform: uppercase; + margin-bottom: 0.22rem; +} + +.gift__row dd { + margin: 0; + color: var(--ink); + font-weight: 700; + line-height: 1.5; + word-break: break-word; +} + +.gift__row--iban dd { + font-size: clamp(1.15rem, 2.3vw, 1.42rem); + letter-spacing: 0.038em; + color: var(--sea-dark); + margin-bottom: 0.38rem; + font-weight: 800; + line-height: 1.35; +} + +.gift__row--iban { + padding-top: 0.04rem; + padding-bottom: 0.82rem; + border-bottom-color: rgba(30, 87, 138, 0.22); +} + +.gift__copy { + width: auto; +} + +.gift__iban-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.55rem 0.75rem; +} + +.gift__feedback { + min-height: 1.15rem; + margin: 0; + color: var(--sea-dark); + font-size: 0.84rem; + font-weight: 700; + opacity: 0.88; +} + .form-status { min-height: 1.4rem; margin: 0; @@ -482,6 +722,24 @@ textarea:focus { grid-template-columns: 1fr; } + .gift__layout { + grid-template-columns: 1fr; + row-gap: 0.88rem; + } + + .gift__layout::before, + .gift__layout::after { + width: min(320px, 100%); + } + + .gift__col--right { + padding-left: 1.15rem; + } + + .gift__heading { + max-width: 100%; + } + .section { padding: 4rem 0; } @@ -538,7 +796,8 @@ textarea:focus { } .form-card, - .details-card { + .details-card, + .gift__layout { padding: 1.2rem; } @@ -550,6 +809,27 @@ textarea:focus { width: 100%; justify-content: center; } + + .calendar-tools__action { + width: 100%; + } + + .gift__layout { + padding: 0; + } + + .gift__copy { + width: 100%; + } + + .gift__iban-actions { + align-items: stretch; + gap: 0.45rem; + } + + .gift__feedback { + width: 100%; + } }