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,99 @@
|
||||
<script lang="ts">
|
||||
import { accordion } from '$lib/actions/accordion';
|
||||
import Icon from '$lib/components/ui/Icon.svelte';
|
||||
import type { FaqItem } from '$lib/types';
|
||||
|
||||
export let title = 'FAQs';
|
||||
export let intro: string | undefined = undefined;
|
||||
export let faqs: FaqItem[];
|
||||
export let emitSchema = true;
|
||||
export let variant: 'panel' | 'plain' = 'panel';
|
||||
|
||||
$: schemaJson = JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer
|
||||
}
|
||||
}))
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{#if emitSchema && faqs.length}
|
||||
{@html `<script type="application/ld+json">${schemaJson}<` + `/script>`}
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<div class:faq-section-plain={variant === 'plain'} class="faq-section">
|
||||
<h2 class="faq-section-heading">
|
||||
<span class="faq-section-icon"><Icon name="fas fa-circle-question" /></span>
|
||||
{title}
|
||||
</h2>
|
||||
{#if intro}
|
||||
<p class="faq-section-intro">{intro}</p>
|
||||
{/if}
|
||||
<div use:accordion class="faq">
|
||||
{#each faqs as faq}
|
||||
<details>
|
||||
<summary>{faq.question}</summary>
|
||||
<p>{faq.answer}</p>
|
||||
</details>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.faq-section-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
border-radius: 12px;
|
||||
background: var(--gw-green);
|
||||
box-shadow: 0 10px 22px rgba(33, 48, 33, 0.16);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.faq-section-icon :global(.icon) {
|
||||
color: var(--yellow);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.faq-section-heading {
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
.faq-section-intro {
|
||||
margin: 0 0 18px;
|
||||
color: #5b6067;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.faq-section-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.faq-section-heading {
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
text-wrap: balance;
|
||||
font-size: clamp(22px, 5.6vw, 26px);
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.faq summary,
|
||||
.faq details p {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user