v4.0.0.1
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
export let booking: BookingContent;
|
||||
export let pagePath = '';
|
||||
$: isCompactContactPage = pagePath === '/contact-us';
|
||||
|
||||
const defaultServices = ['Tiny Gang Pack Walks', 'Solo Walks', 'Puppy Visits', 'Other Services'];
|
||||
$: serviceOptions = booking.serviceOptions && booking.serviceOptions.length > 0
|
||||
@@ -204,6 +205,22 @@
|
||||
return Object.keys(next).length === 0;
|
||||
}
|
||||
|
||||
function validateCompactContactForm(): boolean {
|
||||
const next: Record<string, string> = {};
|
||||
if (!petName.trim()) next.petName = "Please tell us your dog's name.";
|
||||
if (selectedServices.length === 0) next.services = 'Pick at least one service.';
|
||||
if (message.trim().length < 10) {
|
||||
next.message = 'Tell us a little about your dog so we can prepare properly.';
|
||||
}
|
||||
if (!fullName.trim()) next.fullName = 'Please enter your full name';
|
||||
const emailErr = validateEmail(email);
|
||||
if (emailErr) next.email = emailErr;
|
||||
if (!phone.trim()) next.phone = 'Please enter your phone number';
|
||||
if (!location.trim()) next.location = 'Please enter your suburb';
|
||||
errors = next;
|
||||
return Object.keys(next).length === 0;
|
||||
}
|
||||
|
||||
function goNext() {
|
||||
noteInteraction();
|
||||
if (!validateStep(step)) return;
|
||||
@@ -222,7 +239,9 @@
|
||||
|
||||
async function handleSubmit() {
|
||||
noteInteraction();
|
||||
if (!validateStep(2)) return;
|
||||
if (isCompactContactPage) {
|
||||
if (!validateCompactContactForm()) return;
|
||||
} else if (!validateStep(2)) return;
|
||||
|
||||
submitting = true;
|
||||
sendClickedAt = Date.now();
|
||||
@@ -252,7 +271,7 @@
|
||||
journey,
|
||||
referrer: typeof document !== 'undefined' ? document.referrer : '',
|
||||
page: typeof window !== 'undefined' ? window.location.href : '',
|
||||
variant: 'booking-wizard'
|
||||
variant: isCompactContactPage ? 'contact-compact' : 'booking-wizard'
|
||||
})
|
||||
});
|
||||
|
||||
@@ -276,7 +295,7 @@
|
||||
</script>
|
||||
|
||||
<section id="newlead" use:reveal={{ delay: 70 }} class="wiz reveal-block">
|
||||
<div class="wiz-inner">
|
||||
<div class="wiz-inner" class:wiz-inner--compact={isCompactContactPage}>
|
||||
{#if submitted && SuccessModalComponent}
|
||||
<svelte:component
|
||||
this={SuccessModalComponent}
|
||||
@@ -298,43 +317,45 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="wiz-header">
|
||||
<span class="wiz-eyebrow">Free Meet & Greet</span>
|
||||
<h2 class="wiz-title">
|
||||
<span class="wiz-title-plain">{headingParts.plain}</span>
|
||||
{#if headingParts.highlight}
|
||||
{' '}
|
||||
<span class="wiz-title-highlight">{headingParts.highlight}</span>
|
||||
{/if}
|
||||
</h2>
|
||||
<p class="wiz-lead">{leadCopy}</p>
|
||||
</div>
|
||||
|
||||
<div class="wiz-trust" aria-label="Goodwalk dog families">
|
||||
<div class="wiz-avatars" aria-hidden="true">
|
||||
{#each avatarDogs as dog}
|
||||
<span class="wiz-avatar">
|
||||
<img src={dog.image} alt="" loading="lazy" />
|
||||
</span>
|
||||
{/each}
|
||||
{#if !isCompactContactPage}
|
||||
<div class="wiz-header">
|
||||
<span class="wiz-eyebrow">Free Meet & Greet</span>
|
||||
<h2 class="wiz-title">
|
||||
<span class="wiz-title-plain">{headingParts.plain}</span>
|
||||
{#if headingParts.highlight}
|
||||
{' '}
|
||||
<span class="wiz-title-highlight">{headingParts.highlight}</span>
|
||||
{/if}
|
||||
</h2>
|
||||
<p class="wiz-lead">{leadCopy}</p>
|
||||
</div>
|
||||
<div class="wiz-trust-copy">
|
||||
<p>{trustTitle}</p>
|
||||
<span>{trustNote}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wiz-progress" role="progressbar" aria-valuemin="1" aria-valuemax="2" aria-valuenow={step}>
|
||||
<span class="wiz-step-mark" class:active={step >= 1} class:done={step > 1}>
|
||||
<span class="wiz-step-num">1</span>
|
||||
<span class="wiz-step-label">{booking.dogStepLabel || 'Your dog'}</span>
|
||||
</span>
|
||||
<span class="wiz-step-line" class:done={step > 1} aria-hidden="true"></span>
|
||||
<span class="wiz-step-mark" class:active={step === 2}>
|
||||
<span class="wiz-step-num">2</span>
|
||||
<span class="wiz-step-label">{booking.ownerStepLabel || 'Your details'}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="wiz-trust" aria-label="Goodwalk dog families">
|
||||
<div class="wiz-avatars" aria-hidden="true">
|
||||
{#each avatarDogs as dog}
|
||||
<span class="wiz-avatar">
|
||||
<img src={dog.image} alt="" loading="lazy" />
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="wiz-trust-copy">
|
||||
<p>{trustTitle}</p>
|
||||
<span>{trustNote}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wiz-progress" role="progressbar" aria-valuemin="1" aria-valuemax="2" aria-valuenow={step}>
|
||||
<span class="wiz-step-mark" class:active={step >= 1} class:done={step > 1}>
|
||||
<span class="wiz-step-num">1</span>
|
||||
<span class="wiz-step-label">{booking.dogStepLabel || 'Your dog'}</span>
|
||||
</span>
|
||||
<span class="wiz-step-line" class:done={step > 1} aria-hidden="true"></span>
|
||||
<span class="wiz-step-mark" class:active={step === 2}>
|
||||
<span class="wiz-step-num">2</span>
|
||||
<span class="wiz-step-label">{booking.ownerStepLabel || 'Your details'}</span>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form
|
||||
class="wiz-form"
|
||||
@@ -355,10 +376,189 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<article class="wiz-card">
|
||||
<article class="wiz-card" class:wiz-card--compact={isCompactContactPage}>
|
||||
{#key step}
|
||||
<div class="wiz-step" in:fade={{ duration: 200 }}>
|
||||
{#if step === 1}
|
||||
<div class="wiz-step" class:wiz-step--compact={isCompactContactPage} in:fade={{ duration: 200 }}>
|
||||
{#if isCompactContactPage}
|
||||
<span class="wiz-step-eyebrow">Start here</span>
|
||||
<h3 class="wiz-step-heading">Tell us about your dog</h3>
|
||||
<p class="wiz-step-helper">A few details now. We come back with the right next step.</p>
|
||||
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-dog" /> Your dog's name
|
||||
</span>
|
||||
<input
|
||||
bind:value={petName}
|
||||
on:input={() => clearError('petName')}
|
||||
type="text"
|
||||
placeholder="For example, Teddy"
|
||||
class:invalid={errors.petName}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{#if errors.petName}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.petName}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<fieldset class="wiz-fieldset">
|
||||
<legend class="wiz-label">
|
||||
<Icon name="fas fa-paw" /> Which service are you interested in?
|
||||
</legend>
|
||||
<div
|
||||
class="wiz-service-grid"
|
||||
class:wiz-service-grid--compact={isCompactContactPage}
|
||||
class:invalid={errors.services}
|
||||
role="group"
|
||||
aria-label="Service interest"
|
||||
>
|
||||
{#each serviceOptions as service}
|
||||
{@const checked = selectedServices.includes(service)}
|
||||
<button
|
||||
type="button"
|
||||
class="wiz-service"
|
||||
class:wiz-service--compact={isCompactContactPage}
|
||||
class:active={checked}
|
||||
aria-pressed={checked}
|
||||
on:click={() => toggleService(service)}
|
||||
>
|
||||
<span class="wiz-service-check" aria-hidden="true">
|
||||
{#if checked}<Icon name="fas fa-check" />{/if}
|
||||
</span>
|
||||
<span class="wiz-service-text">
|
||||
<span class="wiz-service-label">{service}</span>
|
||||
{#if serviceDescriptions[service]}
|
||||
<span class="wiz-service-desc">{serviceDescriptions[service]}</span>
|
||||
{/if}
|
||||
</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if errors.services}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.services}
|
||||
</span>
|
||||
{/if}
|
||||
</fieldset>
|
||||
|
||||
<div class="wiz-grid-two">
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-user" /> Full name
|
||||
</span>
|
||||
<input
|
||||
bind:value={fullName}
|
||||
on:input={() => clearError('fullName')}
|
||||
type="text"
|
||||
placeholder="Your full name"
|
||||
class:invalid={errors.fullName}
|
||||
autocomplete="name"
|
||||
/>
|
||||
{#if errors.fullName}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.fullName}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-envelope" /> Email
|
||||
</span>
|
||||
<input
|
||||
bind:value={email}
|
||||
on:input={() => clearError('email')}
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
class:invalid={errors.email}
|
||||
autocomplete="email"
|
||||
inputmode="email"
|
||||
/>
|
||||
{#if errors.email}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.email}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-phone" /> Phone
|
||||
</span>
|
||||
<input
|
||||
bind:value={phone}
|
||||
on:input={() => clearError('phone')}
|
||||
type="tel"
|
||||
placeholder="(021) 234 5678"
|
||||
class:invalid={errors.phone}
|
||||
autocomplete="tel"
|
||||
inputmode="tel"
|
||||
/>
|
||||
{#if errors.phone}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.phone}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-location-dot" /> Your suburb
|
||||
</span>
|
||||
<input
|
||||
bind:value={location}
|
||||
on:input={() => clearError('location')}
|
||||
type="text"
|
||||
placeholder="For example, Grey Lynn"
|
||||
class:invalid={errors.location}
|
||||
autocomplete="address-level2"
|
||||
/>
|
||||
{#if errors.location}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.location}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="wiz-field">
|
||||
<span class="wiz-label">
|
||||
<Icon name="fas fa-comment" /> What else should we know?
|
||||
</span>
|
||||
<textarea
|
||||
bind:value={message}
|
||||
on:input={() => clearError('message')}
|
||||
rows="3"
|
||||
placeholder="Age, breed, temperament around other dogs, any health quirks, anything that helps us prepare."
|
||||
class:invalid={errors.message}
|
||||
></textarea>
|
||||
{#if errors.message}
|
||||
<span class="wiz-error">
|
||||
<Icon name="fas fa-circle-exclamation" />
|
||||
{errors.message}
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="wiz-actions">
|
||||
<button type="submit" class="wiz-btn wiz-btn-primary wiz-btn-primary--wide" disabled={submitting}>
|
||||
{#if submitting}
|
||||
Sending
|
||||
{:else}
|
||||
Send my details
|
||||
{/if}
|
||||
<Icon name="fas fa-paper-plane" />
|
||||
</button>
|
||||
</div>
|
||||
{:else if step === 1}
|
||||
<span class="wiz-step-eyebrow">Step one of two</span>
|
||||
<h3 class="wiz-step-heading">Tell us about your dog</h3>
|
||||
<p class="wiz-step-helper">Just the basics. Pick everything you are open to.</p>
|
||||
@@ -556,7 +756,7 @@
|
||||
</article>
|
||||
</form>
|
||||
|
||||
<p class="wiz-reassurance" aria-live="polite">
|
||||
<p class="wiz-reassurance" class:wiz-reassurance--compact={isCompactContactPage} aria-live="polite">
|
||||
<Icon name="fas fa-bolt" />
|
||||
A real reply within 24 hours, usually sooner.
|
||||
</p>
|
||||
@@ -575,6 +775,11 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.wiz-inner--compact {
|
||||
max-width: 60rem;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.wiz-header {
|
||||
text-align: center;
|
||||
margin-bottom: 28px;
|
||||
@@ -780,12 +985,21 @@
|
||||
box-shadow: 0 30px 60px rgba(var(--ink-rgb), 0.08);
|
||||
}
|
||||
|
||||
.wiz-card--compact {
|
||||
max-width: 52rem;
|
||||
padding: clamp(22px, 3vw, 32px);
|
||||
}
|
||||
|
||||
.wiz-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.wiz-step--compact {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.wiz-step-eyebrow {
|
||||
align-self: flex-start;
|
||||
padding: 6px 12px;
|
||||
@@ -831,19 +1045,6 @@
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.wiz-optional {
|
||||
margin-left: 6px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(var(--brand-rgb), 0.06);
|
||||
color: var(--text-subtle);
|
||||
font-family: var(--font-body);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.01em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wiz-field input,
|
||||
.wiz-field textarea {
|
||||
width: 100%;
|
||||
@@ -942,6 +1143,10 @@
|
||||
border-color: rgba(var(--brand-rgb), 0.32);
|
||||
}
|
||||
|
||||
.wiz-service--compact {
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.wiz-service.active {
|
||||
border-color: var(--gw-green);
|
||||
background: rgba(var(--accent-rgb), 0.1);
|
||||
@@ -1022,6 +1227,11 @@
|
||||
background: oklch(0.88 0.18 95);
|
||||
}
|
||||
|
||||
.wiz-btn-primary--wide {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wiz-btn-back {
|
||||
background: transparent;
|
||||
color: var(--text-subtle);
|
||||
@@ -1041,6 +1251,10 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wiz-reassurance--compact {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.wiz-reassurance :global(.icon) {
|
||||
color: var(--yellow);
|
||||
font-size: 13px;
|
||||
@@ -1056,6 +1270,10 @@
|
||||
padding-left: var(--space-container-x-mobile);
|
||||
padding-right: var(--space-container-x-mobile);
|
||||
}
|
||||
|
||||
.wiz-inner--compact {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@@ -1085,6 +1303,24 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wiz-service-grid--compact {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wiz-service--compact {
|
||||
min-height: 100%;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.wiz-service--compact .wiz-service-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wiz-card--compact {
|
||||
padding: 20px 18px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
.wiz-grid-two {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user