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:
2026-06-17 21:51:29 +12:00
co-authored by Claude Opus 4.8
parent 7d5b72e1d3
commit 0e88e7bdac
82 changed files with 4543 additions and 914 deletions
+59
View File
@@ -41,9 +41,13 @@ class OnboardingSubmission(BaseSubmission):
medicalNotes: str = ""
accessInstructions: str = ""
vetName: str
vetAddress: str
vetPhone: str
emergencyContactName: str
emergencyContactPhone: str
regularFleaTickTreatment: str = ""
petInsurance: str = ""
petInsuranceOwnerExpenseAccepted: bool = False
councilRegistrationConfirmed: bool = False
vaccinationsConfirmed: bool = False
emergencyVetConsent: bool = False
@@ -54,15 +58,33 @@ class OnboardingSubmission(BaseSubmission):
class WelcomePackEmailRequest(BaseModel):
email: EmailStr
# Owner-set subject line. Defaults to "Goodwalk Onboarding - <Dog Name>" when
# blank (resolved server-side so scheduled sends keep a sensible subject too).
subject: str = ""
serviceType: str
priceDetails: str
startDate: str
preview: bool = False
previewRecipients: list[EmailStr] = []
# ISO 8601 local datetime (e.g. "2026-06-20T14:30"). When set on a non-preview
# request, the welcome email is queued for reliable delivery at that time
# instead of being sent immediately.
scheduledFor: str | None = None
class ScheduledEmailCancelRequest(BaseModel):
id: str
class ScheduledEmailRescheduleRequest(BaseModel):
id: str
scheduledFor: str
class BirthdayEmailRequest(BaseModel):
email: EmailStr
preview: bool = False
previewRecipients: list[EmailStr] = []
class BirthdayAutoSendRequest(BaseModel):
@@ -81,6 +103,42 @@ class ClientStatusUpdate(BaseModel):
reason: str = ""
class ClientProfileUpdate(BaseModel):
email: EmailStr
nextEmail: EmailStr
fullName: str
phone: str = ""
address: str = ""
dogName: str
class ResetOnboardingRequest(BaseModel):
"""Owner-initiated reset: keep the client's saved details but mark their
onboarding incomplete so they can sign in and complete the new form
(used for clients imported from the legacy Gravity Forms data)."""
email: EmailStr
class NewClientRequest(BaseModel):
"""Owner-initiated client creation (e.g. leads from Instagram / Facebook
that never came through the public enquiry form). Registers the email so
the client can access the onboarding form, and seeds a profile."""
email: EmailStr
fullName: str
phone: str = ""
address: str = ""
dogName: str = ""
dogBreed: str = ""
# Where the client came from, e.g. "Instagram", "Facebook", "Referral".
source: str = ""
# When true (and the MYOB integration is configured), also create the client
# as a customer contact in MYOB. Non-fatal: a MYOB failure never blocks the
# local client from being created.
createInMyob: bool = False
class ContractSubmission(BaseSubmission):
address: str
dogName: str
@@ -126,3 +184,4 @@ class SendMessageRequest(BaseModel):
fontId: str = "system"
recipients: list[EmailStr] = []
preview: bool = False
previewRecipients: list[EmailStr] = []