Zedule.
FEATURE · CALENDAR

The calendar your day actually fits in

Five views, twelve gestures, snap-to-15-min, drag-anywhere reschedule, right-click + long-press menus, agenda + mini-month + print, all-day events, per-service accent colour, locale-aware time formatting, mobile single-staff swipe. Built for service businesses that want their calendar to keep up with the day, not slow it down.

01
AT A GLANCE

Five views, one click apart

Day, Week, Month, Agenda, plus a flat list mode for the day. Each view answers a different question and toggling between them is a single keystroke. The Day view is multi-resource — every staff member gets their own column so you see who's doing what without scrolling.

  • Day · multi-staff column grid with per-event accent colour
  • Week · 7-day grouped overview with an event-density bar
  • Month · serif date numerals, accent dot on today, click any day to drill in
  • Agenda · flat chronological list of the week, grouped by day with sticky headers
  • List · a per-day timeline with status badges and inline status actions
  • Keyboard: D / W / M / A switches view, T jumps to today, ←/→ steps the date
Day view with four staff columns, side-by-side overlapping appointments, and per-service accent colours
02
DRAG TO RESCHEDULE

Move an event by dragging it. Anywhere.

Grab any appointment, drag it to a new time, drop. The card follows your cursor smoothly at one-pixel resolution while a dashed accent outline shows where it'll land on a 15-minute snap. The inline label on the card updates live so you see the new time before you let go. No modal, no confirmation, no 'are you sure'.

  • Smooth follow at 1 px; snap to 15 min on release (or 5 min at 4× zoom)
  • Dashed snap-target ghost shows the eventual landing slot
  • Drag across staff columns to reassign — same gesture, multi-staff aware
  • Drag the bottom edge to resize duration; resize handle is 12 px on touch, 8 px on mouse
  • Hold Alt while dragging to duplicate instead of move (cyan COPY ghost)
  • Optimistic update + rollback on the rare server conflict
Calendar showing a card mid-drag with the snap-target outline visible at the destination
03
CREATE

Click an empty slot to book in seconds

Move your cursor over the grid and a faint dashed block previews the 15-minute slot you're hovering with the time inside it. Click it — the New Appointment modal opens with the date, time, and staff already filled in. Click-and-drag instead and the modal opens with the dragged duration prefilled.

  • Hover preview block + a more prominent snap line at the cursor's row
  • Click → New appointment with date / time / staff prefilled
  • Click-and-drag → New appointment with the dragged duration
  • Hover label respects 12-hour vs 24-hour preference
  • Right-click an empty cell → context menu with New appointment / Block this time
Empty cell hover preview showing a faint dashed block with the snapped time label
04
EVENT DETAILS

Click an event for full details; click Edit to change anything

Tap any event to see the booking: customer (with click-to-call and click-to-email), service, date and time, staff, status, notes. Click Edit and the modal flips to inline editing — change the service (search-and-pick from your catalog), the staff, the date or time, the status, the notes. Save patches the appointment in place.

  • Click-to-call / click-to-email on the customer's phone and email
  • Status pill with quick-action buttons: Mark complete, No-show, Cancel booking
  • Edit mode: dropdowns for service + staff + status, date+time pickers, notes
  • Service change auto-recomputes duration; end_at adjusts unless overridden
  • Right-click an event → context menu (View / Mark complete / No-show / Copy info / Cancel)
  • Long-press on touch is the right-click equivalent
Appointment details modal showing customer contacts, service, when, staff, and footer status actions
05
OVERLAP, ON PURPOSE

Overbook deliberately when you mean to

The system warns when a slot conflicts with another booking. It doesn't block. Walk-ins, double-ups, two-chair workflows — operators say yes when the customer's standing in front of them. The customer-facing booking widget keeps the hard guard so customers can never overbook themselves into someone else's slot; only the dashboard can choose to overlap.

  • Soft warning panel: 'Slot overlaps an existing booking … Book anyway'
  • Operator-only: dashboard role gates allow_overlap; the public widget never sets it
  • Per-service capacity (max_concurrent) still respected for group sessions
  • Cross-staff drag goes through the same validator — overlap warns, doesn't block
New Appointment modal showing the soft 'Slot overlaps an existing booking' warning with a Book anyway button
06
CUSTOMER PICKER

Existing customer? Type two letters.

Start typing a customer name and Zedule searches your existing roster — name, phone, email match. Pick a result and the contact fields populate automatically; the appointment binds to that customer's record. No more silent duplicates from misspelt emails. Type a brand-new name and it falls back to creating a new customer.

  • Live search after 2+ characters, debounced 200 ms
  • Suggestion rows show name, phone/email, total visit count
  • Picking a customer locks the contact fields and shows a 'linked to existing customer' badge
  • Editing any contact field after picking unlocks → back to creating new
  • Backend uses customer_id for picked, customer object for new
New Appointment modal showing the customer-name typeahead with three matching suggestions
07
ZOOM + TICKS

1× / 2× / 4×, with tick density that matches

Three zoom levels — 56, 112, or 224 px per hour. The grid's sub-hour gridlines and the time gutter sub-labels scale together: 30-min ticks at 1×, 15-min at 2×, 5-min at 4×. At 4× the snap granularity also tightens to 5 min so the cursor lines up with what you can see.

  • Zoom toolbar: − / current level / + (also keyboard +/−)
  • Faint tick lines at 30 / 15 / 5 min intervals depending on zoom
  • Time-gutter labels show :30 (1×), :15 :30 :45 (4×) in monospaced subtle text
  • Snap granularity: 15 min default, 5 min at 4× zoom
  • Per-tenant preference, persisted in localStorage
Calendar at 4× zoom with 5-minute tick lines visible between the hour rows and :15/:30/:45 labels in the gutter
08
BUSINESS HOURS

Working hours visible, not just enforced

Each staff member has working hours per weekday. The calendar grid renders non-working slots with a soft grey wash so you can see at a glance when each person is on, who's off today, and where to fit a walk-in.

  • Per-staff working hours editor in the Staff page
  • Non-working slots get an 8% grey overlay in that staff's column
  • Visible hour range derived from the union of all staff hours, padded ±1 hour
  • Whole-day-off staff: their column is fully shaded but still selectable
  • Now-line: red horizontal line at the current minute, only on today, ticks every minute
Calendar Day view with non-working hours softly shaded grey on three of four staff columns
09
PER-SERVICE COLOUR

Pick an accent for each service

Each service can carry an accent hex. Cards on the calendar pick up that colour as the left-edge stripe and the card-background wash. A salon owner uses one accent for cuts, another for color, a third for treatments — the day's mix is legible at a glance without reading any text.

  • Service editor: 8 preset swatches plus a custom hex input
  • Card border + 8% background blend use the service accent
  • Falls back to staff-tinted background when no accent is set
  • Per-service icon picker is roadmap; current v1 ships colour only
Service editor showing 8 colour preset swatches and a custom-hex picker
10
ALL-DAY

Whole-day events get their own row

Mark an appointment as all-day in the New Appointment modal — the time picker hides, the booking spans 00:00–23:59, and the calendar shows it as a full-width pill in a strip above the hour grid. The strip only renders when there's at least one all-day event today, so it never costs vertical space when not needed.

  • All-day checkbox in New Appointment modal
  • Top strip with one full-width pill per all-day event per staff column
  • Click pill → opens the same details modal as a timed event
  • Backend stores is_all_day; opportunistic ALTER for older tenants
Calendar Day view with the All-day strip visible at the top, one pill labelled 'Conference' on the third staff column
11
MOBILE

Single-staff column with swipe between staff

On a phone, the calendar collapses to one staff member at a time. A horizontal strip of avatars sits above the grid — tap to switch, or swipe horizontally on the grid edge. Long-press replaces right-click for context menus. The resize handle grows to 12 px so you can grab it with a thumb.

  • Auto-detected at viewports ≤ 720 px
  • MobileStaffStrip avatars with appointment-count badges
  • Horizontal swipe on grid edges → prev / next day
  • Long-press 500 ms → context menu (replaces right-click)
  • All drag / resize / create gestures work on touch
  • Default view on first mobile load = Agenda
Mobile calendar view with one staff column visible and the avatar strip above showing other staff to switch to
12
CONTEXT + KEYBOARD

Right-click everywhere, shortcuts for everything

Right-click an event for View / Mark complete / Mark no-show / Copy customer info / Cancel. Right-click an empty cell for New appointment / Block this time. Keyboard shortcuts cover navigation, view-switching, and zoom — no clicking the toolbar to step through dates.

  • Right-click event → 5-item action menu
  • Right-click empty cell → New / Block
  • Long-press on touch fires the same menu
  • T = today · ← → = prev / next · D W M A = view · + / − = zoom
  • Esc closes any open menu or modal
Calendar Week view with a right-click context menu open over an event showing five actions
13
I18N

9 locales, 12 / 24-hour, week-start preference

Set the operator's locale, hour format, and week-start in Settings → Scheduling → Calendar display. Time labels, date labels, currency formatting, and the mini-month picker all honour the preference. Stored in localStorage per tenant — switching businesses can keep different settings.

  • 9 locales: en-US, en-GB, en-IN, fr-FR, de-DE, es-ES, pt-BR, ja-JP, zh-CN
  • 12-hour or 24-hour clock — independent of locale (some en-US users prefer 24h)
  • Week-start: Sunday / Monday / Saturday
  • All hover labels, gutter labels, agenda day headings, modal date strings format-aware
  • Customer-facing emails respect business locale separately
Settings → Scheduling tab with the new Calendar display section showing locale, hour format, and week-start dropdowns
14
MINI-MONTH PICKER

Click the date to jump anywhere

Click the date label in the toolbar and a small month grid pops below it. Today is highlighted, days with events are dotted, prev/next chevrons walk months without closing the popover. Click any day to jump the main view there.

  • 6×7 month grid in a popover anchored to the toolbar date
  • Today: accent ring · selected: filled ink · others: plain
  • Days with appointments show a small dot beneath the date number
  • Honours the week-start preference
  • Closes on outside-click, Esc, or selecting a day
Mini-month picker popover open below the toolbar date label, showing today highlighted and dots on busy days
15
PRINT

Print the day on one clean page

Toolbar Print button calls window.print() and a tuned @media print stylesheet kicks in: nav and chrome hidden, calendar expanded to the page width, status colours preserved (with a graceful greyscale fallback). Sections avoid splitting across pages.

  • Print button in the toolbar
  • @media print strips nav, sidebar, FAB, action buttons
  • Sections set page-break-inside: avoid
  • Status pills keep their colour in colour-print, fall back to grey otherwise
Browser print preview showing the calendar Day view full-width with no nav chrome

What it doesn't do — yet

We ship features when they're worth using, not when they fit a comparison chart. Here's what's deliberately not in the calendar today:

  • 3-Day side-by-side multi-resource grid. Week view + Agenda cover most multi-day workflows; a true 3-day grid is a substantial layout refactor we'll do once a real operator asks for it.
  • Recurring appointment series with RRULE. Block-time has weekly repeat; appointments don't yet. Workaround: alt-drag to duplicate.
  • Calendar embed for customers. Customer-facing booking page is its own surface; an embedded read-only calendar isn't shipped.
  • Drag-cross-day in week view. Reschedule via the details modal in week view; intra-day drag is the supported path.

Everything in the long list above is shipped, on production, today.

Forty-five days from now,
you'll either be running your bookings here
or you'll have lost nothing.

Start free