Complete scheduling flow for contractors — hub, calendar views (month/week/day/agenda), availability settings, booking configuration, and onboarding call booking.
No pending invitations
Appointment requests from homeowners will appear here
SchedulingHome — contractor scheduling hub
useRouter, useThemedStyles(createStyles)
No upcoming appointments
Your scheduled appointments will appear here
No pending invitations
Appointment requests from homeowners will appear here
SchedulingHome — empty / new contractor state
Empty component renders for both sections
ContractorCalendar — MonthView sub-component
useQuery(api.contractor.scheduling.listAppointments), viewMode: 'month'
WeekView — time-grid with positioned blocks
weekAppointments, getAppointmentPosition(), scrollViewRef
DayView — single-day timeline with current time indicator
selectedDayAppointments, navigateDay(), currentTimePosition
AgendaView — grouped by date, filterable list
groupedAppointments, upcomingAppointments, statusFilter: 'pending'
AvailabilitySettings — contractor weekly schedule
useMutation(api.contractor.scheduling.setAvailability), schedule state
BookingPageSettings — public page config
useQuery(api.poster.availability.getBookingSettings), updateSettings mutation
OnboardingCallBookingScreen — date + time selection
useMutation(api.contractor.scheduling.scheduleOnboardingCall), selectedDate/Time state
OnboardingCallBookingScreen — isAlreadyScheduled state
profile?.user?.metadata?.onboardingCallScheduled === true
// Contractor scheduling
SchedulingHome → app/(contractor)/.../scheduling/index.tsx
ContractorCalendar → app/(contractor)/.../scheduling/calendar.tsx
MonthView | WeekView | DayView | AgendaView
AppointmentCard | AppointmentDetails
QuickCreateForm | RescheduleForm
AvailabilitySettings → .../scheduling/availability.tsx
OnboardingCallBooking → .../scheduling/onboarding-call.tsx
// Poster scheduling (parallel)
PosterSchedulingHub → app/(poster)/scheduling/index.tsx
PosterAvailability → app/(poster)/scheduling/availability.tsx
BookingPageSettings → app/(poster)/scheduling/booking-page.tsx
PosterCalendarTab → app/(poster)/(tabs)/calendar.tsx
// Contractor queries
api.contractor.scheduling.listAppointments
args: { limit: number }
returns: { items: ConvexAppointment[] }
// Contractor mutations
api.scheduling.appointments.confirm
api.scheduling.appointments.complete
api.scheduling.appointments.cancel
api.scheduling.appointments.markNoShow
api.scheduling.googleCalendar.syncAppointment
api.contractor.scheduling.scheduleOnboardingCall
// Poster queries
api.poster.availability.getMyAvailability
api.poster.availability.getBookingSettings
api.poster.appointments.getAppointmentCounts
api.poster.appointments.getUpcomingAppointments
// Poster mutations
api.poster.availability.setBulkAvailability
api.poster.availability.updateBookingSettings
ViewMode = 'month' | 'week' | 'day' | 'agenda'
AppointmentType =
'in_home' | 'site_visit' | 'estimate'
| 'job_work' | 'consultation'
AppointmentStatus =
'pending' | 'confirmed' | 'completed'
| 'cancelled' | 'no_show'
DaySchedule {
isWorking: boolean
startTime: string // "08:00"
endTime: string // "17:00"
breaks: Break[]
}
BookingSettings {
bookingSlug: string
bookingPageEnabled: boolean
defaultDuration: number // minutes
bufferBefore/After: number
minNotice: number // hours
maxAdvance: number // days
autoAccept: boolean
}
// Contractor palette (terracotta accent)
in_home → terracotta (In-Home Review)
site_visit → info/blue (Site Visit)
estimate → success/green (Estimate)
job_work → adminPrimary/purple (Job Work)
consultation → warning/orange (Consultation)
// Status badge mapping
pending → warning
confirmed → success
completed → default
cancelled → error
no_show → error
// Google Calendar sync
syncToGoogleMutation
shows warning toast if not connected
links to Settings → Integrations → Google