v4.0.5
Component architecture - Reorganise src/lib/components into pages/, sections/, ui/ subdirectories and update all imports across routes and tests. Owner admin dashboard (+2k lines) - Scheduled welcome-pack emails: durable queue with cancel/reschedule and a background sender loop (SCHEDULED_CHECK_INTERVAL_SECONDS, default 60s). - Custom welcome-pack subject line, preview recipients, and client BCC. - Add-client, edit client profile, and reset-onboarding flows. - "View as client" onboarding preview (owner impersonation, dry-run submit). - Tabbed owner welcome route (/owner/welcome/[[tab]]). MYOB integration - New mail_api/myob.py: create new clients as MYOB AccountRight customer contacts. Disabled until all MYOB_* env vars are set (no-op otherwise). Onboarding - New "Does your dog resource guard?" Yes/No question in the Behaviour step. - Persist vetAddress, flea/tick, and pet-insurance fields server-side. Misc - vite config, new hooks.ts, responsive CSS tweaks, deploy/docker config, mail-api README and start-dev.ps1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/ui/Icon.svelte';
|
||||
import type { FooterContent, LinkItem } from '$lib/types';
|
||||
import { locationPages } from '$lib/content/locations';
|
||||
|
||||
export let footer: FooterContent;
|
||||
|
||||
|
||||
const socialLinks: LinkItem[] = [
|
||||
{ label: 'Instagram', href: 'https://www.instagram.com/goodwalk.nz/', external: true },
|
||||
{ label: 'Facebook', href: 'https://facebook.com/goodwalk.nz', external: true },
|
||||
{ label: 'Google', href: 'https://g.page/r/CUsvrWPhkYrAEB0', external: true }
|
||||
];
|
||||
|
||||
const aboutLink: LinkItem = { label: 'About Us', href: '/about' };
|
||||
|
||||
function scrollToTop() {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function withAboutLink(links: LinkItem[]) {
|
||||
if (links.some((link) => link.href === aboutLink.href || link.label === aboutLink.label)) {
|
||||
return links;
|
||||
}
|
||||
|
||||
const contactIndex = links.findIndex((link) => link.href === '/contact-us');
|
||||
|
||||
if (contactIndex === -1) {
|
||||
return [...links, aboutLink];
|
||||
}
|
||||
|
||||
return [...links.slice(0, contactIndex), aboutLink, ...links.slice(contactIndex)];
|
||||
}
|
||||
|
||||
$: navigationLinks = withAboutLink(footer.navigationLinks);
|
||||
</script>
|
||||
|
||||
<footer data-track-location="footer">
|
||||
<div class="footer-inner">
|
||||
<div class="footer-brand">
|
||||
<enhanced:img
|
||||
src="$lib/images/goodwalk-auckland-dog-walking-logo.png"
|
||||
alt="Goodwalk – Auckland dog walking service logo"
|
||||
class="footer-logo"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
<p>{footer.brandText}</p>
|
||||
<div class="footer-social-cluster">
|
||||
<div class="footer-social-invite" aria-hidden="true">
|
||||
<span>Join the fun</span>
|
||||
<Icon name="fas fa-arrow-turn-down" className="footer-social-invite-arrow" />
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href={socialLinks[0].href} target="_blank" rel="noopener" aria-label="Instagram" data-track-event="social_click" data-track-type="footer_social" data-track-label="Footer Instagram">
|
||||
<Icon name="fab fa-instagram" />
|
||||
</a>
|
||||
<a href={socialLinks[1].href} target="_blank" rel="noopener" aria-label="Facebook" data-track-event="social_click" data-track-type="footer_social" data-track-label="Footer Facebook">
|
||||
<Icon name="fab fa-facebook-f" />
|
||||
</a>
|
||||
<a href={socialLinks[2].href} target="_blank" rel="noopener" aria-label="Google" data-track-event="social_click" data-track-type="footer_social" data-track-label="Footer Google">
|
||||
<Icon name="fab fa-google" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if footer.email || footer.phone}
|
||||
<div class="footer-contact">
|
||||
{#if footer.email}
|
||||
<a href="mailto:{footer.email}" class="footer-contact-link" data-track-event="contact_click" data-track-type="email" data-track-label="Footer email">
|
||||
<Icon name="fas fa-envelope" />
|
||||
{footer.email}
|
||||
</a>
|
||||
{/if}
|
||||
{#if footer.phone}
|
||||
<a href="tel:{footer.phone.replace(/[^0-9+]/g, '')}" class="footer-contact-link" data-track-event="contact_click" data-track-type="phone" data-track-label="Footer phone">
|
||||
<Icon name="fas fa-phone" />
|
||||
{footer.phone}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="footer-explore" data-track-location="footer_explore">
|
||||
<p class="footer-col-label">Explore Goodwalk</p>
|
||||
<ul class="footer-nav">
|
||||
{#each navigationLinks as link}
|
||||
<li>
|
||||
<a
|
||||
href={link.href}
|
||||
target={link.external ? '_blank' : undefined}
|
||||
rel={link.external ? 'noopener' : undefined}
|
||||
data-track-event="nav_click"
|
||||
data-track-type="footer_nav"
|
||||
data-track-label={link.label}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-locations" data-track-location="footer_locations">
|
||||
<p class="footer-col-label">
|
||||
<a href="/locations">Areas we serve</a>
|
||||
</p>
|
||||
<ul class="footer-nav">
|
||||
{#each locationPages as loc}
|
||||
<li><a href="/locations/{loc.slug}" data-track-event="nav_click" data-track-type="footer_location" data-track-label={loc.suburb}>{loc.suburb}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<span>{footer.copyright}</span>
|
||||
<nav class="footer-legal" data-track-location="footer_legal">
|
||||
<a href="/terms-and-conditions" data-track-event="nav_click" data-track-type="footer_legal" data-track-label="Terms and Conditions">Terms & Conditions</a>
|
||||
<a href="/privacy-policy" data-track-event="nav_click" data-track-type="footer_legal" data-track-label="Privacy Policy">Privacy Policy</a>
|
||||
</nav>
|
||||
<button type="button" class="footer-back-top" aria-label="Back to top" on:click={scrollToTop} data-track-event="nav_click" data-track-type="footer_back_to_top" data-track-label="Back to top">
|
||||
↑ Back to top
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
Reference in New Issue
Block a user