202 lines
5.2 KiB
Python
202 lines
5.2 KiB
Python
"""Pydantic request/response models for the mail API."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, EmailStr
|
|
|
|
|
|
class BaseSubmission(BaseModel):
|
|
fullName: str
|
|
email: EmailStr
|
|
phone: str
|
|
website: str = ""
|
|
formStartedAt: int | None = None
|
|
visitStartedAt: int | None = None
|
|
pageEnteredAt: int | None = None
|
|
firstInteractionAt: int | None = None
|
|
sendClickedAt: int | None = None
|
|
referrer: str = ""
|
|
page: str = ""
|
|
|
|
|
|
class BookingSubmission(BaseSubmission):
|
|
enquiryType: str = "booking"
|
|
petName: str = ""
|
|
location: str = ""
|
|
message: str = ""
|
|
services: list[str] = []
|
|
stepChanges: int = 0
|
|
journey: list[str] = []
|
|
|
|
|
|
class OnboardingSubmission(BaseSubmission):
|
|
address: str
|
|
dogName: str
|
|
dogBreed: str
|
|
dogAge: str = ""
|
|
servicesNeeded: list[str] = []
|
|
temperament: str = ""
|
|
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
|
|
termsAccepted: bool = False
|
|
signatureDataUrl: str
|
|
submissionSnapshot: dict[str, Any] = {}
|
|
|
|
|
|
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
|
|
dogId: str | None = None
|
|
preview: bool = False
|
|
previewRecipients: list[EmailStr] = []
|
|
|
|
|
|
class BirthdayAutoSendRequest(BaseModel):
|
|
email: EmailStr
|
|
dogId: str | None = None
|
|
enabled: bool
|
|
|
|
|
|
# Client lifecycle status. Soft-delete only — every client stays on file so the
|
|
# history is preserved for future newsletter / retention work.
|
|
ClientLifecycleStatus = Literal["active", "paused", "cancelled", "archived"]
|
|
|
|
|
|
class ClientStatusUpdate(BaseModel):
|
|
email: EmailStr
|
|
status: ClientLifecycleStatus
|
|
reason: str = ""
|
|
|
|
|
|
class ClientProfileUpdate(BaseModel):
|
|
email: EmailStr
|
|
nextEmail: EmailStr
|
|
fullName: str
|
|
phone: str = ""
|
|
address: str = ""
|
|
dogName: str
|
|
dogBreed: str = ""
|
|
dogAge: 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 = ""
|
|
dogAge: 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 ClientDogUpsertRequest(BaseModel):
|
|
email: EmailStr
|
|
dogId: str | None = None
|
|
dogName: str
|
|
dogBreed: str = ""
|
|
dogAge: str = ""
|
|
birthdayAutoSend: bool = False
|
|
|
|
|
|
class ContractSubmission(BaseSubmission):
|
|
address: str
|
|
dogName: str
|
|
dogBreed: str
|
|
dogAge: str = ""
|
|
serviceType: str
|
|
startDate: str
|
|
walkFrequency: str = ""
|
|
additionalNotes: str = ""
|
|
agreeServiceTerms: bool = False
|
|
agreeCancellation: bool = False
|
|
agreePayment: bool = False
|
|
agreeEmergency: bool = False
|
|
agreeLiability: bool = False
|
|
agreeAccuracy: bool = False
|
|
signatureDataUrl: str
|
|
|
|
|
|
class RenderMessageRequest(BaseModel):
|
|
templateId: str
|
|
heading: str = ""
|
|
body: str = ""
|
|
ctaLabel: str = ""
|
|
ctaUrl: str = ""
|
|
subHeading: str = ""
|
|
highlightText: str = ""
|
|
signOff: str = ""
|
|
footerNote: str = ""
|
|
fontId: str = "system"
|
|
|
|
|
|
class SendMessageRequest(BaseModel):
|
|
templateId: str
|
|
subject: str
|
|
heading: str = ""
|
|
body: str = ""
|
|
ctaLabel: str = ""
|
|
ctaUrl: str = ""
|
|
subHeading: str = ""
|
|
highlightText: str = ""
|
|
signOff: str = ""
|
|
footerNote: str = ""
|
|
fontId: str = "system"
|
|
recipients: list[EmailStr] = []
|
|
preview: bool = False
|
|
previewRecipients: list[EmailStr] = []
|