02examples
Shift scheduler
Five people, one week, as timeline lanes — drag to schedule, drag across rows to reassign a shift to someone else.
Five people, one week, opening to close, each on their own row. A shift is a bar coloured by the person working it; days off are wider bars covering the whole day, labelled "All day" and hatched because they are marked free. The legend counts rostered hours from the live events — resize Ben's Saturday and his number moves.
liveShift scheduler
loading
uses
How it works
view="timeline"withlanes={LANES}— oneLaneper person, rather than the day columns aweekview would give this a calendar-shaped approximation of (Resource timeline).timelineWindowis controlled:start/endpin the window to this calendar week exactly (slotMinutes: 60), so the roster never drifts — there's no toolbar ‹ › here, because a controlled window doesn't move with it (the twotimelineWindowmodes).- The
LaneAdapteris hand-rolled, notbyResourceId(): reassigning a shift to someone else's row doesn't just repointpersonId—assignalso repaints the bar's colour and retitles it to the new person, so a dragged shift reads correctly on arrival instead of carrying its old owner's name along.assign's contract is "the patch that puts this event onlaneId", and nothing limits that patch to the fieldlaneOfreads (a worked custom adapter). - Days off are ordinary events with
allDay: truespanning midnight to midnight (dateOnlyAt/dateOnlyAfter) — the timeline has no all-day lane to promote them into, so they render as a bar the width of the day;busy: falseis what draws the hatch. workingHours({ start: 6, end: 22 }) tints outside the café's hours on every day the window covers, anddensity="compact"shrinks the bars to fit five rows without scrolling.events/onEventsChangemake the roster React state (Controlled state);hoursByPersonindata.tssumswallMinutesBetweenover it on every render, which is why the legend follows a resize.
Take it further
- Put each person's row height up with
Lane.heightfor anyone who regularly gets double-covered, the same lever the room booking example uses for its busiest room (the overflow cap). - Add
onCommitto push each change to a rota service and let a rejection spring the shift back (Optimistic sync). - Flag understaffed hours:
findConflictsgives you overlaps, and the gaps are the complement.