Appointments

Scheduling system for building inspections, customer meetings, and other project-related events. Supports proposal-based booking where customers pick time slots through the Portal.

Data model note (2026-06): there is no standalone appointments table. Appointments live in two places: appointment_proposals (the proposal/booking flow, anchored on a quote) and appointment-type entries in activity_logs (the confirmed appointment record). Project and building context is resolved through the quote (appointment-service.ts).

Source Files

LayerPath
Schemabackend/src/db/schema.ts
Servicebackend/src/services/appointment-service.ts
Portal Pagefrontend/src/pages/portal/PortalAppointmentView.tsx
Queriesfrontend/src/lib/queries/appointmentQueries.ts
Portal Queriesfrontend/src/lib/queries/portalAppointmentQueries.ts

Database Tables

TablePurpose
appointment_proposalsProposal/booking flow — time slot options sent to customers, acceptance tracking, portal token
activity_logsConfirmed appointments recorded as appointment-type activity entries (see Audit Logs)

Key Fields — Appointment Proposals

FieldTypeNotes
quoteIduuid FKAnchor entity — the quote this appointment belongs to (required, cascade)
phaseId / taskIduuidWorkflow context the proposal was created from
createdByUserIduuid FKStaff member who created the proposal
statusvarcharopen, accepted, declined, expired
proposedSlotsjsonbArray of slots: dateTime, duration (min), location (onsite/virtual/phone + address or URL)
acceptedSlotIndexintegerWhich slot the customer chose (0-based)
customerAcceptedAttimestampWhen the customer accepted
expiresAttimestampProposal expiry (default 7 days)
portalTokenvarchar(64)Customer portal access token (SHA-256 hash, unique)
notestextInternal notes

Key Fields — Appointment Activity Entries

Acceptance writes an activity_logs row (e.g., type appointment_accepted) carrying:

FieldTypeNotes
appointmentDatetimestampConfirmed date and time (from the accepted slot)
appointmentTypevarcharphone, onsite, virtual
scheduledDatedateOperator-set organisation date for week planning (I#1865)
buildingId / projectId / quoteIduuid FKContext resolved via the quote

Proposal-Based Booking

  1. Staff creates an appointment_proposals row with multiple time slot options
  2. Customer receives a notification with a tokenized link to the Portal
  3. Customer views proposals on PortalAppointmentView and selects a slot
  4. Acceptance sets status: accepted + acceptedSlotIndex and writes an appointment activity entry to activity_logs
  5. Both parties receive confirmation via Notifications

Workflow Integration

Appointments are created as part of Workflows tasks. Typical workflow tasks that generate appointments:

Task TypeAppointment Type
Site inspectionBuilding survey visit
Customer consultationAdvisory meeting
Results presentationFinal review meeting

The workflow task tracks the appointment status and can auto-advance when the appointment is completed.

Relationships

Appointment Proposal *──1 Quote (anchor; project/building resolved via quote)
Appointment Proposal *──1 Users (createdByUserId)
Appointment Proposal ──> Workflow Phase/Task (phaseId, taskId)
Appointment Proposal ──> Activity Log entry (on acceptance)

Frontend

  • Internal view — appointments appear inline within Workflows task cards and on project detail pages
  • Portal viewPortalAppointmentView.tsx provides the customer-facing proposal picker
  • Queries — separate query files for internal (appointmentQueries.ts) and portal (portalAppointmentQueries.ts) contexts

Workflows | Quotes | Projects | Buildings | Audit Logs | Portal | Notifications | Users | Service Layer Pattern