Only show the booking calendars after the lead form is sent
/contact/book/ now needs ?submitted=1 (set in the GHL form redirect) to load the calendars; other visitors get a prompt that links to the form. The page is noindex and out of the sitemap, the contact page card points to the form, and visitors are sent back to the language they filled the form in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,4 +61,4 @@ Importa el repo en Vercel. `vercel.json` ya define el build (`npm run build`), l
|
|||||||
- **Press:** falta el link del artículo de Gulf Insider y confirmar su titular exacto (`FEATURE` en `src/pages/press.mjs`).
|
- **Press:** falta el link del artículo de Gulf Insider y confirmar su titular exacto (`FEATURE` en `src/pages/press.mjs`).
|
||||||
- **Korrith:** falta el cargo de Dylan (el tercer campo, vacío, en `src/pages/portfolio.mjs`).
|
- **Korrith:** falta el cargo de Dylan (el tercer campo, vacío, en `src/pages/portfolio.mjs`).
|
||||||
- **Páginas fuera del alcance actual:** Playbook y Poetry no forman parte del sitio. Sus secciones se muestran sin botón; si se crean esas páginas, se añade el botón con su texto original del documento.
|
- **Páginas fuera del alcance actual:** Playbook y Poetry no forman parte del sitio. Sus secciones se muestran sin botón; si se crean esas páginas, se añade el botón con su texto original del documento.
|
||||||
- **Formulario y agenda (GoHighLevel):** los IDs están en `SITE.ghl` (`src/layout.mjs`). En GoHighLevel, el formulario "Dylan – Lead Form" debe redirigir a `https://dylanmazzei.com/contact/book/` al enviarse.
|
- **Formulario y agenda (GoHighLevel):** los IDs están en `SITE.ghl` (`src/layout.mjs`). En GoHighLevel, el formulario "Dylan – Lead Form" debe redirigir a `https://dylanmazzei.com/contact/book/?submitted=1` al enviarse. Sin `?submitted=1`, `/contact/book/` muestra un aviso para llenar el formulario en vez de los calendarios (es solo un filtro en el navegador: los calendarios de GHL siguen siendo públicos, así que el cobro y la disponibilidad se controlan en GHL). La página no se indexa ni sale en el sitemap.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ for (const name of pages) {
|
|||||||
await mkdir(dirname(file), { recursive: true });
|
await mkdir(dirname(file), { recursive: true });
|
||||||
await writeFile(file, fingerprint(html));
|
await writeFile(file, fingerprint(html));
|
||||||
}
|
}
|
||||||
urls.push(def.path);
|
if (!def.noindex) urls.push(def.path);
|
||||||
console.log('built', def.path);
|
console.log('built', def.path);
|
||||||
}
|
}
|
||||||
for (const [l, set] of Object.entries(missing)) {
|
for (const [l, set] of Object.entries(missing)) {
|
||||||
|
|||||||
+5
-2
@@ -1242,7 +1242,6 @@
|
|||||||
"Give me enough context to understand what you’re working on, where things stand, and what you’d like to discuss.": "أعطني ما يكفي من السياق لأفهم ما تعمل عليه، وأين وصلت الأمور، وما الذي تود مناقشته.",
|
"Give me enough context to understand what you’re working on, where things stand, and what you’d like to discuss.": "أعطني ما يكفي من السياق لأفهم ما تعمل عليه، وأين وصلت الأمور، وما الذي تود مناقشته.",
|
||||||
"Dylan – Lead Form": "ديلان – نموذج التواصل",
|
"Dylan – Lead Form": "ديلان – نموذج التواصل",
|
||||||
"Rather Put It on the Calendar?": "تفضّل حجز موعد مباشرة؟",
|
"Rather Put It on the Calendar?": "تفضّل حجز موعد مباشرة؟",
|
||||||
"If you already know a conversation makes sense, skip the back-and-forth and find a time that works.": "إن كنت تعرف بالفعل أن المحادثة منطقية، فتجاوز المراسلات المتبادلة واختر وقتًا يناسبك.",
|
|
||||||
"NOT SURE WHERE TO START?": "لست متأكدًا من أين تبدأ؟",
|
"NOT SURE WHERE TO START?": "لست متأكدًا من أين تبدأ؟",
|
||||||
"See What I’m Working On First.": "اطّلع أولًا على ما أعمل عليه.",
|
"See What I’m Working On First.": "اطّلع أولًا على ما أعمل عليه.",
|
||||||
"If you want a little context before reaching out, explore the companies I operate today, see the businesses and exits that shaped my journey, or learn how I work with founders.": "إن أردت بعض السياق قبل التواصل، استكشف الشركات التي أديرها اليوم، أو اطّلع على الأعمال وصفقات التخارج التي شكّلت رحلتي، أو تعرّف على طريقة عملي مع المؤسسين.",
|
"If you want a little context before reaching out, explore the companies I operate today, see the businesses and exits that shaped my journey, or learn how I work with founders.": "إن أردت بعض السياق قبل التواصل، استكشف الشركات التي أديرها اليوم، أو اطّلع على الأعمال وصفقات التخارج التي شكّلت رحلتي، أو تعرّف على طريقة عملي مع المؤسسين.",
|
||||||
@@ -1309,5 +1308,9 @@
|
|||||||
"Building around new opportunities created by AI.": "البناء على الفرص الجديدة التي يخلقها الذكاء الاصطناعي.",
|
"Building around new opportunities created by AI.": "البناء على الفرص الجديدة التي يخلقها الذكاء الاصطناعي.",
|
||||||
"Creating technology designed for real classrooms.": "ابتكار تقنيات مصممة للفصول الدراسية الحقيقية.",
|
"Creating technology designed for real classrooms.": "ابتكار تقنيات مصممة للفصول الدراسية الحقيقية.",
|
||||||
"Solving security challenges for growing businesses.": "حل تحديات الأمن للشركات النامية.",
|
"Solving security challenges for growing businesses.": "حل تحديات الأمن للشركات النامية.",
|
||||||
"Turning product ideas into businesses built around real customers.": "تحويل أفكار المنتجات إلى أعمال مبنية حول عملاء حقيقيين."
|
"Turning product ideas into businesses built around real customers.": "تحويل أفكار المنتجات إلى أعمال مبنية حول عملاء حقيقيين.",
|
||||||
|
"Send the form above and you’ll pick a time that works right after, no back-and-forth.": "أرسل النموذج أعلاه وستختار مباشرة بعده الوقت الذي يناسبك، دون مراسلات متبادلة.",
|
||||||
|
"Tell Me a Bit About You First.": "أخبرني قليلًا عن نفسك أولًا.",
|
||||||
|
"Send the short form on the contact page and you’ll pick a time for your call right after.": "أرسل النموذج القصير في صفحة التواصل وستختار موعد مكالمتك مباشرة بعده.",
|
||||||
|
"GO TO THE FORM": "انتقل إلى النموذج"
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -1242,7 +1242,6 @@
|
|||||||
"Give me enough context to understand what you’re working on, where things stand, and what you’d like to discuss.": "Dame suficiente contexto para entender en qué estás trabajando, en qué punto están las cosas y de qué te gustaría hablar.",
|
"Give me enough context to understand what you’re working on, where things stand, and what you’d like to discuss.": "Dame suficiente contexto para entender en qué estás trabajando, en qué punto están las cosas y de qué te gustaría hablar.",
|
||||||
"Dylan – Lead Form": "Dylan – Formulario de contacto",
|
"Dylan – Lead Form": "Dylan – Formulario de contacto",
|
||||||
"Rather Put It on the Calendar?": "¿Prefieres agendarlo directamente?",
|
"Rather Put It on the Calendar?": "¿Prefieres agendarlo directamente?",
|
||||||
"If you already know a conversation makes sense, skip the back-and-forth and find a time that works.": "Si ya sabes que una conversación tiene sentido, sáltate los mensajes de ida y vuelta y encuentra un horario que te funcione.",
|
|
||||||
"NOT SURE WHERE TO START?": "¿NO SABES POR DÓNDE EMPEZAR?",
|
"NOT SURE WHERE TO START?": "¿NO SABES POR DÓNDE EMPEZAR?",
|
||||||
"See What I’m Working On First.": "Mira primero en qué estoy trabajando.",
|
"See What I’m Working On First.": "Mira primero en qué estoy trabajando.",
|
||||||
"If you want a little context before reaching out, explore the companies I operate today, see the businesses and exits that shaped my journey, or learn how I work with founders.": "Si quieres un poco de contexto antes de escribir, explora las empresas que opero hoy, mira los negocios y ventas que marcaron mi camino o conoce cómo trabajo con fundadores.",
|
"If you want a little context before reaching out, explore the companies I operate today, see the businesses and exits that shaped my journey, or learn how I work with founders.": "Si quieres un poco de contexto antes de escribir, explora las empresas que opero hoy, mira los negocios y ventas que marcaron mi camino o conoce cómo trabajo con fundadores.",
|
||||||
@@ -1309,5 +1308,9 @@
|
|||||||
"Building around new opportunities created by AI.": "Construyendo sobre las nuevas oportunidades que crea la IA.",
|
"Building around new opportunities created by AI.": "Construyendo sobre las nuevas oportunidades que crea la IA.",
|
||||||
"Creating technology designed for real classrooms.": "Creando tecnología pensada para aulas reales.",
|
"Creating technology designed for real classrooms.": "Creando tecnología pensada para aulas reales.",
|
||||||
"Solving security challenges for growing businesses.": "Resolviendo retos de seguridad para negocios en crecimiento.",
|
"Solving security challenges for growing businesses.": "Resolviendo retos de seguridad para negocios en crecimiento.",
|
||||||
"Turning product ideas into businesses built around real customers.": "Convirtiendo ideas de producto en negocios construidos alrededor de clientes reales."
|
"Turning product ideas into businesses built around real customers.": "Convirtiendo ideas de producto en negocios construidos alrededor de clientes reales.",
|
||||||
|
"Send the form above and you’ll pick a time that works right after, no back-and-forth.": "Envía el formulario de arriba y justo después eliges el horario que te funcione, sin mensajes de ida y vuelta.",
|
||||||
|
"Tell Me a Bit About You First.": "Primero cuéntame un poco sobre ti.",
|
||||||
|
"Send the short form on the contact page and you’ll pick a time for your call right after.": "Envía el formulario corto de la página de contacto y justo después eliges el horario de tu llamada.",
|
||||||
|
"GO TO THE FORM": "IR AL FORMULARIO"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -7,8 +7,6 @@ export const SITE = {
|
|||||||
pressEmail: 'press@dylanmazzei.com',
|
pressEmail: 'press@dylanmazzei.com',
|
||||||
linkedin: 'https://www.linkedin.com/in/dylanmazzei/',
|
linkedin: 'https://www.linkedin.com/in/dylanmazzei/',
|
||||||
instagram: 'https://www.instagram.com/thedylanmazzei',
|
instagram: 'https://www.instagram.com/thedylanmazzei',
|
||||||
// Replace with the real scheduling link (Calendly, Cal.com, etc.).
|
|
||||||
calendar: '/contact/book/',
|
|
||||||
// GoHighLevel (LeadConnector) widgets. Contacts land in Dylan's GHL account.
|
// GoHighLevel (LeadConnector) widgets. Contacts land in Dylan's GHL account.
|
||||||
ghl: {
|
ghl: {
|
||||||
form: 'xm10A7alZrxeKDtkMkD2', // "Dylan – Lead Form"
|
form: 'xm10A7alZrxeKDtkMkD2', // "Dylan – Lead Form"
|
||||||
@@ -281,5 +279,6 @@ ${scripts}
|
|||||||
export const GHL_SCRIPT = '<script src="https://link.msgsndr.com/js/form_embed.js" defer></script>';
|
export const GHL_SCRIPT = '<script src="https://link.msgsndr.com/js/form_embed.js" defer></script>';
|
||||||
export const ghlForm = (id, name) =>
|
export const ghlForm = (id, name) =>
|
||||||
`<iframe class="ghl-frame" src="https://api.leadconnectorhq.com/widget/form/${id}" id="inline-${id}" title="${name}" data-layout="{'id':'INLINE'}" data-trigger-type="alwaysShow" data-trigger-value="" data-activation-type="alwaysActivated" data-activation-value="" data-deactivation-type="neverDeactivate" data-deactivation-value="" data-form-name="${name}" data-layout-iframe-id="inline-${id}" data-form-id="${id}"></iframe>`;
|
`<iframe class="ghl-frame" src="https://api.leadconnectorhq.com/widget/form/${id}" id="inline-${id}" title="${name}" data-layout="{'id':'INLINE'}" data-trigger-type="alwaysShow" data-trigger-value="" data-activation-type="alwaysActivated" data-activation-value="" data-deactivation-type="neverDeactivate" data-deactivation-value="" data-form-name="${name}" data-layout-iframe-id="inline-${id}" data-form-id="${id}"></iframe>`;
|
||||||
|
// Calendars start without a src: book.js loads them once the visitor has sent the lead form.
|
||||||
export const ghlCalendar = (id, title) =>
|
export const ghlCalendar = (id, title) =>
|
||||||
`<iframe class="ghl-frame ghl-cal" src="https://api.leadconnectorhq.com/widget/booking/${id}" id="${id}_cal" title="${title}" scrolling="no"></iframe>`;
|
`<iframe class="ghl-frame ghl-cal" data-src="https://api.leadconnectorhq.com/widget/booking/${id}" id="${id}_cal" title="${title}" scrolling="no"></iframe>`;
|
||||||
|
|||||||
+33
-3
@@ -1,6 +1,9 @@
|
|||||||
import { icon, eyebrow, SITE, ghlCalendar, GHL_SCRIPT } from '../layout.mjs';
|
import { icon, eyebrow, SITE, ghlCalendar, GHL_SCRIPT } from '../layout.mjs';
|
||||||
|
|
||||||
// Step 2 of the GoHighLevel booking flow. The lead form redirects here after submit.
|
// Step 2 of the GoHighLevel booking flow. The lead form redirects here after submit,
|
||||||
|
// to /contact/book/?submitted=1. Visitors who did not come from the form see a prompt
|
||||||
|
// to fill it in instead of the calendars. This only hides the page: the calendars are
|
||||||
|
// still public GHL widgets, so the real limits (payment, availability) live in GHL.
|
||||||
// Copy mirrors the "Book Your Call" step Gabriela built in GoHighLevel.
|
// Copy mirrors the "Book Your Call" step Gabriela built in GoHighLevel.
|
||||||
const calls = [
|
const calls = [
|
||||||
{ id: 'free', tag: 'FREE', h: 'Free 15-min intro call with Dylan’s team', p: 'Discover how we can support your business goals and see if we are the right match for your journey.', cta: 'Book free call', cal: SITE.ghl.freeCalendar, icon: 'users' },
|
{ id: 'free', tag: 'FREE', h: 'Free 15-min intro call with Dylan’s team', p: 'Discover how we can support your business goals and see if we are the right match for your journey.', cta: 'Book free call', cal: SITE.ghl.freeCalendar, icon: 'users' },
|
||||||
@@ -9,6 +12,26 @@ const calls = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
path: '/contact/book/',
|
path: '/contact/book/',
|
||||||
|
noindex: true,
|
||||||
|
head: `<script>
|
||||||
|
(function () {
|
||||||
|
// Gate: ?submitted=1 (from the GHL redirect) unlocks the calendars for this browser tab.
|
||||||
|
var root = document.documentElement, ok = false;
|
||||||
|
try {
|
||||||
|
var q = new URLSearchParams(location.search);
|
||||||
|
if (q.has('submitted')) {
|
||||||
|
sessionStorage.setItem('dm-form-sent', '1');
|
||||||
|
// GHL redirects to one URL, so send the visitor back to the language they filled the form in.
|
||||||
|
var lang = sessionStorage.getItem('dm-lang') || root.lang;
|
||||||
|
var want = (lang === 'en' ? '' : '/' + lang) + '/contact/book/';
|
||||||
|
if (want !== location.pathname && /^(en|es|ar)$/.test(lang)) { location.replace(want + location.hash); return; }
|
||||||
|
history.replaceState(null, '', location.pathname + location.hash);
|
||||||
|
}
|
||||||
|
ok = sessionStorage.getItem('dm-form-sent') === '1';
|
||||||
|
} catch (e) { ok = true; }
|
||||||
|
root.setAttribute('data-gate', ok ? 'open' : 'locked');
|
||||||
|
})();
|
||||||
|
</script>`,
|
||||||
title: 'Book Your Call | Dylan Mazzei',
|
title: 'Book Your Call | Dylan Mazzei',
|
||||||
description: 'Book a free 15-minute intro call with Dylan Mazzei’s team or a 15-minute 1:1 session with Dylan.',
|
description: 'Book a free 15-minute intro call with Dylan Mazzei’s team or a 15-minute 1:1 session with Dylan.',
|
||||||
scripts: GHL_SCRIPT + '\n<script src="/assets/book.js" defer></script>',
|
scripts: GHL_SCRIPT + '\n<script src="/assets/book.js" defer></script>',
|
||||||
@@ -18,10 +41,17 @@ export default {
|
|||||||
<div class="stack center anim" style="align-items:center;text-align:center;max-width:760px;margin:0 auto">
|
<div class="stack center anim" style="align-items:center;text-align:center;max-width:760px;margin:0 auto">
|
||||||
<nav class="crumbs" aria-label="Breadcrumb"><a href="/contact/">Contact</a>${icon('chev')}<span>Book your call</span></nav>
|
<nav class="crumbs" aria-label="Breadcrumb"><a href="/contact/">Contact</a>${icon('chev')}<span>Book your call</span></nav>
|
||||||
${eyebrow('BOOK YOUR CALL')}
|
${eyebrow('BOOK YOUR CALL')}
|
||||||
|
<div class="gate-open stack center" style="align-items:center">
|
||||||
<h1 id="bk-hero" class="h-display-lg">Thanks! Now choose your call.</h1>
|
<h1 id="bk-hero" class="h-display-lg">Thanks! Now choose your call.</h1>
|
||||||
<p class="lead">Pick the option that fits you best.</p>
|
<p class="lead">Pick the option that fits you best.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="calls anim" role="tablist" aria-label="Call options">
|
<div class="gate-locked stack center" style="align-items:center">
|
||||||
|
<h2 class="h-display-lg">Tell Me a Bit About You First.</h2>
|
||||||
|
<p class="lead">Send the short form on the contact page and you’ll pick a time for your call right after.</p>
|
||||||
|
<a class="btn btn-primary" href="/contact/#inquiry">GO TO THE FORM<span class="go">${icon('arrow')}</span></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="calls anim gate-open" role="tablist" aria-label="Call options">
|
||||||
${calls
|
${calls
|
||||||
.map(
|
.map(
|
||||||
(c, i) => `<button class="call${c.featured ? ' call-vip' : ''}" type="button" role="tab" id="tab-${c.id}" aria-controls="cal-${c.id}" aria-selected="${i === 0}" data-call="${c.id}">
|
(c, i) => `<button class="call${c.featured ? ' call-vip' : ''}" type="button" role="tab" id="tab-${c.id}" aria-controls="cal-${c.id}" aria-selected="${i === 0}" data-call="${c.id}">
|
||||||
@@ -36,7 +66,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section" id="schedule" aria-label="Choose a time" style="padding-top:0">
|
<section class="section gate-open" id="schedule" aria-label="Choose a time" style="padding-top:0">
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
${calls
|
${calls
|
||||||
.map(
|
.map(
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ export default {
|
|||||||
<div class="ghl-wrap ghl-wrap-form">${ghlForm(SITE.ghl.form, 'Dylan – Lead Form')}</div>
|
<div class="ghl-wrap ghl-wrap-form">${ghlForm(SITE.ghl.form, 'Dylan – Lead Form')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="cal-card" href="${SITE.calendar}">
|
<a class="cal-card" href="#inquiry">
|
||||||
<div><h3>Rather Put It on the Calendar?</h3><p>If you already know a conversation makes sense, skip the back-and-forth and find a time that works.</p></div>
|
<div><h3>Rather Put It on the Calendar?</h3><p>Send the form above and you’ll pick a time that works right after, no back-and-forth.</p></div>
|
||||||
<span class="go">${icon('calendar')}</span>
|
<span class="go">${icon('calendar')}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
(function () {
|
(function () {
|
||||||
|
if (document.documentElement.getAttribute('data-gate') !== 'open') return;
|
||||||
|
document.querySelectorAll('iframe[data-src]').forEach(function (f) { f.src = f.getAttribute('data-src'); });
|
||||||
var tabs = document.querySelectorAll('[data-call]');
|
var tabs = document.querySelectorAll('[data-call]');
|
||||||
var select = function (id, scroll) {
|
var select = function (id, scroll) {
|
||||||
tabs.forEach(function (t) {
|
tabs.forEach(function (t) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
(function () {
|
(function () {
|
||||||
window.__site = true;
|
window.__site = true;
|
||||||
var root = document.documentElement;
|
var root = document.documentElement;
|
||||||
|
// The booking page uses this to keep visitors in the language they filled the form in.
|
||||||
|
if (!/\/contact\/book\//.test(location.pathname)) { try { sessionStorage.setItem('dm-lang', root.lang || 'en'); } catch (e) {} }
|
||||||
// Translations for the current page's language, written into the page by the build.
|
// Translations for the current page's language, written into the page by the build.
|
||||||
var T = function (s) { return (window.__i18n && window.__i18n[s]) || s; };
|
var T = function (s) { return (window.__i18n && window.__i18n[s]) || s; };
|
||||||
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
|||||||
@@ -1055,6 +1055,7 @@ h1, h2, h3, h4 { font-weight: 600; }
|
|||||||
.cal-panel { max-width: 980px; margin: 0 auto; border-radius: 24px; }
|
.cal-panel { max-width: 980px; margin: 0 auto; border-radius: 24px; }
|
||||||
@media (max-width: 760px) { .calls { grid-template-columns: 1fr; } .call { padding: 24px; } }
|
@media (max-width: 760px) { .calls { grid-template-columns: 1fr; } .call { padding: 24px; } }
|
||||||
#schedule .wrap { position: relative; }
|
#schedule .wrap { position: relative; }
|
||||||
|
[data-gate="locked"] .gate-open, [data-gate="open"] .gate-locked, html:not([data-gate]) .gate-locked { display: none !important; }
|
||||||
.cal-panel[data-off] { position: absolute; left: -10000px; top: 0; width: min(980px, 100%); visibility: hidden; pointer-events: none; }
|
.cal-panel[data-off] { position: absolute; left: -10000px; top: 0; width: min(980px, 100%); visibility: hidden; pointer-events: none; }
|
||||||
.ghl-wrap-form { min-height: 460px; }
|
.ghl-wrap-form { min-height: 460px; }
|
||||||
.ghl-wrap-cal { min-height: 720px; }
|
.ghl-wrap-cal { min-height: 720px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user