Files
NextChair/docs/calendar.md
T
Stefan Lazic a8a8d1e045
CI / ci (push) Canceled after 0s
Updated Calendard
The calendar is now react-big-calendar.
2026-10-08 11:01:32 +01:00

56 lines
2.8 KiB
Markdown

# Calendar
A living reference for the day calendar on `/staff` (a barber's own day) and
`/owner/calendar` (everyone, one column per barber). Both render
`src/features/staff/components/day-calendar.tsx`.
## What it's built on
[react-big-calendar](https://github.com/jquense/react-big-calendar) (MIT) with
its drag-and-drop addon, restyled to match the app in `day-calendar.css`
(scoped under `.day-cal`). It was chosen over DayPilot Lite mainly because
per-barber columns, a current-time line and sideways scrolling on a phone all
work out of the box; FullCalendar and Schedule-X put per-resource columns
behind a paid licence.
Only the day view is used. The agenda (list) view is a separate component.
## Time zones — read this before touching it
The library works in JavaScript `Date`s, which carry the **browser's** time
zone; the app's scheduling data is **business-local minutes since midnight on
a business-local date**. So the calendar never sees a real instant: times go in
as "floating" dates (minutes laid onto one fixed day — `calendar-time.ts`) and
a drop comes back out as plain minutes. The real date and the business time
zone are applied on the server in `moveCalendarAppointmentAction`.
## Drag to reschedule
Turned on per page with the `reschedule` prop (`"owner"` or `"staff"`).
- A drop **never saves on its own.** It opens a confirmation ("From 11:00 AM
with Leo → To 1:00 PM with Leo… the customer will be notified"), because a
reschedule emails the customer.
- `"owner"`: any appointment in the business, and it can be dropped into
another barber's column (a reassignment). `"staff"`: their own bookings, along
the time axis only.
- Only `pending_approval`, `confirmed` and `arrived` bookings can be moved —
the same statuses `rescheduleBooking` accepts. Breaks, time off, and finished
or in-progress bookings are not draggable. Resizing (changing the length) is
off: duration comes from the services.
- **The server is the gate.** `moveCalendarAppointmentAction` delegates to the
existing `ownerRescheduleAppointmentAction` / `rescheduleStaffAppointmentAction`,
so working hours, breaks, overlaps, the staff member's services, the business's
minimum-notice rule, the audit log, notifications, reminders and Google
Calendar sync all behave exactly as they do from the appointment screen. A
rejected move shows the reason inside the dialog and nothing is saved.
- The calendar itself does not stop you dropping onto a break or outside
someone's hours — it just fails the server check.
## Phones
Columns keep a 190px minimum and the grid scrolls sideways, with the hour
labels pinned on the left and the header row scrolling with it. Dragging was
verified with a mouse only; the library has touch handlers but this has not been
tried on a real touch screen. The appointment screen's Reschedule always works.