---
title: "Javascript Timeline Restaurant Shift Scheduling Example | Mobiscroll"
component: "Timeline"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/timeline/restaurant-shift-management"
---

# Restaurant Shift Scheduling - JavaScript Timeline demo

Build a restaurant shift planning interface with the Javascript Timeline — named shift columns with live occupancy counts and hierarchical staff groups by role. Plain JavaScript API.

- [Live demo](https://demo.mobiscroll.com/javascript/timeline/restaurant-shift-management)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-javascript/tree/main/src/demos/eventcalendar/timeline/restaurant-shift-management)
- This demo in other frameworks: [jQuery](https://demo.mobiscroll.com/jquery/timeline/restaurant-shift-management.md), [Angular](https://demo.mobiscroll.com/angular/timeline/restaurant-shift-management.md), [React](https://demo.mobiscroll.com/react/timeline/restaurant-shift-management.md), [Vue](https://demo.mobiscroll.com/vue/timeline/restaurant-shift-management.md)

## Description

With the combination of the [timeline summary mode/event listing](https://demo.mobiscroll.com/javascript/timeline/event-listing), [time slots](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-slots) and the [time slot templating](https://demo.mobiscroll.com/javascript/timeline/shift-template) features along with [hierarchical resources](https://demo.mobiscroll.com/javascript/timeline/resource-grouping-hierarchy) you can create a restaurant shift planning UI.

The example features templating, dynamic occupancy count (how many employees are working on a specific shift), dynamically turning shifts on/off, switching between week and day view and much more. The [header features custom components](https://demo.mobiscroll.com/javascript/scheduler/customizing-header) for filtering the view.

Try signing somone up for work by double clicking on an empty shift position.

## What this demo shows

- A desktop weekly timeline for restaurant shift scheduling, with Monday to Friday working days arranged horizontally and a hierarchical resource tree on the left.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between weeks and jump back to the current day.
- **Header filters:** Checkbox controls in the header let users show or hide individual shift types.
- **View switching:** A header select control lets users switch between Day and Week timeline views. Changing the view updates the timeline layout.
- **Week view:** The fixed date strip shows the selected work week from Monday to Friday, using the `DD DDD MMM YYYY` date format, with the current date highlighted.
- **Shift columns:** The timeline is divided into named work shifts instead of standard hourly intervals.
- **Shift structure:** Each day repeats the same shift columns: Night (2 AM - 10 AM), Breakfast (8 AM - 12 PM), Lunch (11 AM - 3 PM), Dinner (4 PM - 11 PM), and After hours (11 PM - 1 AM).
- **Shift headers:** Each shift header shows the shift name, time range, and occupancy indicators with icons and counts for how many people from each resource category are assigned to that shift.
- **Resources:** The left side shows expandable and collapsible resource groups for Barista, Bartenders, Chefs, Cleaners, Cooks, Hosts, Managers, Servers, and Sommeliers.
- **Resource styling:** Each parent resource group has its own color and representative icon.
- **Resource hierarchy:** Individual staff members are organized under their corresponding job categories.
- **Event cards:** Shift assignments appear as events in the matching shift column for each employee row.
- **Event colors:** Event colors visually distinguish assignments across resource groups.
- **Event creation:** Users can create new shift assignments by double-clicking an empty shift position on an individual employee row.
- **Event interaction:** Events are highlighted on hover.
- **Event selection:** Clicking an event selects and highlights it.
- **Vertical scrolling:** The resource list scrolls vertically so many employees can be shown in the same timeline.
- **Horizontal scrolling:** The timeline supports horizontal scrolling for navigating across multiple days and shift columns.

## Best for

- **Restaurant scheduling:** Planning staff coverage across predefined restaurant shifts.
- **Hospitality workforce planning:** Assigning employees to structured service periods such as breakfast, lunch, dinner, and after-hours work.
- **Retail staffing:** Managing teams where employees are scheduled into named shift blocks rather than arbitrary time slots.
- **Shift-based resource planning:** Showing role groups, individual employees, and occupancy counts in the same scheduling view.
- **Dense team schedules:** Working with many employees in a hierarchical timeline that supports both vertical and horizontal scrolling.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-themeVariant) - option
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-data) - option
- [`resources`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-resources) - option
- [`slots`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-slots) - option
- [`clickToCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-clickToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToMove) - option
- [`renderSlot`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderSlot) - renderer
- [`renderHeader`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHeader) - renderer
- [`renderResource`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderResource) - renderer
- [`extendDefaultEvent`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-extendDefaultEvent) - option
- [`onEventCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventCreate) - event
- [`onEventUpdate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onEventUpdate) - event
- [`updateEvent`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-updateEvent) - method
- [`getEvents`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-getEvents) - method
- [`inputElement`](https://mobiscroll.com/docs/javascript/select/api#opt-inputElement) - option
- [`touchUi`](https://mobiscroll.com/docs/javascript/select/api#opt-touchUi) - option
- [`onChange`](https://mobiscroll.com/docs/javascript/select/api#event-onChange) - event
- [`setOptions`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setOptions) - method

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

var calendar;
var resources = [
  {
    id: 1,
    name: 'Barista',
    icon: '☕️',
    eventCreation: false,
    children: [
      {
        id: 11,
        name: 'Jude Chester',
        color: '#1dab2f',
      },
      {
        id: 12,
        name: 'Willis Kane',
        color: '#1dab2f',
      },
      {
        id: 13,
        name: 'Ryan Melicent',
        color: '#1dab2f',
      },
    ],
  },
  {
    id: 2,
    name: 'Bartenders',
    icon: '🍸',
    eventCreation: false,
    children: [
      {
        id: 21,
        name: 'Derek Austyn',
        color: '#4981d6',
      },
      {
        id: 22,
        name: 'Merv Kenny',
        color: '#4981d6',
      },
      {
        id: 23,
        name: 'Theo Calanthia',
        color: '#4981d6',
      },
    ],
  },
  {
    id: 3,
    name: 'Chefs',
    icon: '👩‍🍳',
    eventCreation: false,
    children: [
      {
        id: 31,
        name: 'Ford Kaiden',
        color: '#d6d145',
      },
      {
        id: 32,
        name: 'Jewell Ryder',
        color: '#d6d145',
      },
      {
        id: 33,
        name: 'Dory Edie',
        color: '#d6d145',
      },
    ],
  },
  {
    id: 4,
    name: 'Cleaners',
    icon: '🧹',
    eventCreation: false,
    children: [
      {
        id: 41,
        name: 'Breanne Lorinda',
        color: '#ff1717',
      },
      {
        id: 42,
        name: 'Jenifer Kalyn',
        color: '#ff1717',
      },
      {
        id: 43,
        name: 'Natalie Racquel',
        color: '#ff1717',
      },
      {
        id: 44,
        name: 'Kaylin Toni',
        color: '#ff1717',
      },
      {
        id: 45,
        name: 'Gray Kestrel',
        color: '#ff1717',
      },
      {
        id: 46,
        name: 'Florence Amy',
        color: '#ff1717',
      },
    ],
  },
  {
    id: 5,
    name: 'Cooks',
    icon: '🍲',
    eventCreation: false,
    children: [
      {
        id: 51,
        name: 'Meredith Chantelle',
        color: '#f7961e',
      },
      {
        id: 52,
        name: 'Jon Drake',
        color: '#f7961e',
      },
      {
        id: 53,
        name: 'Carlyn Dorothy',
        color: '#f7961e',
      },
      {
        id: 54,
        name: 'Isadora Lyric',
        color: '#f7961e',
      },
    ],
  },
  {
    id: 6,
    name: 'Hosts',
    icon: '🛎',
    eventCreation: false,
    children: [
      {
        id: 61,
        name: 'Layton Candace',
        color: '#7056ff',
      },
      {
        id: 62,
        name: 'Sylvia Cale',
        color: '#7056ff',
      },
    ],
  },
  {
    id: 7,
    name: 'Managers',
    icon: '👩‍💼',
    eventCreation: false,
    children: [
      {
        id: 71,
        name: 'Fred Valdez',
        color: '#3a8700',
      },
      {
        id: 72,
        name: 'Antonia Cindra',
        color: '#3a8700',
      },
      {
        id: 73,
        name: 'Gerry Irma',
        color: '#3a8700',
      },
    ],
  },
  {
    id: 8,
    name: 'Servers',
    icon: '🤵',
    eventCreation: false,
    children: [
      {
        id: 81,
        name: 'Reg Izabelle',
        color: '#e25dd2',
      },
      {
        id: 82,
        name: 'Adrianna Sawyer',
        color: '#e25dd2',
      },
      {
        id: 83,
        name: 'Lou Andie',
        color: '#e25dd2',
      },
      {
        id: 84,
        name: 'Leon Porter',
        color: '#e25dd2',
      },
    ],
  },
  {
    id: 9,
    name: 'Sommeliers',
    icon: '🍷',
    eventCreation: false,
    children: [
      {
        id: 91,
        name: 'Yancy Dustin',
        color: '#34c8e0',
      },
      {
        id: 92,
        name: 'Sierra Clark',
        color: '#34c8e0',
      },
    ],
  },
];

var data = [
  // Showing partial data. Download full source.
  {
    start: '2026-10-23T02:00',
    end: '2026-10-23T10:00',
    title: 'Derek',
    resource: 21,
    slot: 1,
  },
  {
    start: '2026-10-23T02:00',
    end: '2026-10-23T10:00',
    title: 'Florence',
    resource: 46,
    slot: 1,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Willis',
    resource: 12,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Jewell',
    resource: 32,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Jenifer',
    resource: 42,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Jon',
    resource: 52,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Fred',
    resource: 71,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Adrianna',
    resource: 82,
    slot: 2,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T12:00',
    title: 'Jude',
    resource: 11,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Jewell',
    resource: 32,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Gray',
    resource: 45,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Isadora',
    resource: 54,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Antonia',
    resource: 72,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Adrianna',
    resource: 82,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Leon',
    resource: 84,
    slot: 3,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T15:00',
    title: 'Yancy',
    resource: 91,
    slot: 3,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Ryan',
    resource: 13,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Merv',
    resource: 22,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Ford',
    resource: 31,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Dory',
    resource: 33,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Breanne',
    resource: 41,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Meredith',
    resource: 51,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Carlyn',
    resource: 53,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Layton',
    resource: 61,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Gerry',
    resource: 73,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Reg',
    resource: 81,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Lou',
    resource: 83,
    slot: 4,
  },
  {
    start: '2026-10-23T16:00',
    end: '2026-10-23T23:00',
    title: 'Sierra',
    resource: 92,
    slot: 4,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T11:59',
    title: 'Derek',
    resource: 21,
    slot: 5,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T11:59',
    title: 'Natalie',
    resource: 43,
    slot: 5,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T11:59',
    title: 'Kaylin',
    resource: 44,
    slot: 5,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T11:59',
    title: 'Fred',
    resource: 71,
    slot: 5,
  },
  {
    start: '2026-10-24T02:00',
    end: '2026-10-24T10:00',
    title: 'Theo',
    resource: 23,
    slot: 1,
  },
  {
    start: '2026-10-24T02:00',
    end: '2026-10-24T10:00',
    title: 'Jenifer',
    resource: 42,
    slot: 1,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Jude',
    resource: 11,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Jewell',
    resource: 32,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Florence',
    resource: 46,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Jon',
    resource: 52,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Gerry',
    resource: 73,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Adrianna',
    resource: 82,
    slot: 2,
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T12:00',
    title: 'Jude',
    resource: 11,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Dory',
    resource: 33,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Natalie',
    resource: 43,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Meredith',
    resource: 51,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Fred',
    resource: 71,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Lou',
    resource: 83,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Leon',
    resource: 84,
    slot: 3,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T15:00',
    title: 'Sierra',
    resource: 92,
    slot: 3,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Willis',
    resource: 12,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Derek',
    resource: 21,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Ford',
    resource: 31,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Dory',
    resource: 33,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Kaylin',
    resource: 44,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Meredith',
    resource: 51,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Isadora',
    resource: 54,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Sylvia',
    resource: 62,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Antonia',
    resource: 72,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Reg',
    resource: 81,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Lou',
    resource: 83,
    slot: 4,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T23:00',
    title: 'Sierra',
    resource: 92,
    slot: 4,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T11:59',
    title: 'Derek',
    resource: 21,
    slot: 5,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T11:59',
    title: 'Natalie',
    resource: 43,
    slot: 5,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T11:59',
    title: 'Kaylin',
    resource: 44,
    slot: 5,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T11:59',
    title: 'Fred',
    resource: 71,
    slot: 5,
  },
  {
    start: '2026-10-25T02:00',
    end: '2026-10-25T10:00',
    title: 'Merv',
    resource: 22,
    slot: 1,
  },
  {
    start: '2026-10-25T02:00',
    end: '2026-10-25T10:00',
    title: 'Florence',
    resource: 46,
    slot: 1,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Ryan',
    resource: 13,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Jewell',
    resource: 32,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Florence',
    resource: 46,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Carlyn',
    resource: 53,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Fred',
    resource: 71,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Leon',
    resource: 84,
    slot: 2,
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T12:00',
    title: 'Jude',
    resource: 11,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Ford',
    resource: 31,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Natalie',
    resource: 43,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Carlyn',
    resource: 53,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Gerry',
    resource: 73,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Lou',
    resource: 83,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Leon',
    resource: 84,
    slot: 3,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T15:00',
    title: 'Yancy',
    resource: 91,
    slot: 3,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Willis',
    resource: 12,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Merv',
    resource: 22,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Jewell',
    resource: 32,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Dory',
    resource: 33,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Gray',
    resource: 45,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Jon',
    resource: 52,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Isadora',
    resource: 54,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Layton',
    resource: 61,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Antonia',
    resource: 72,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Reg',
    resource: 81,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Lou',
    resource: 83,
    slot: 4,
  },
  {
    start: '2026-10-25T16:00',
    end: '2026-10-25T23:00',
    title: 'Sierra',
    resource: 92,
    slot: 4,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T11:59',
    title: 'Derek',
    resource: 21,
    slot: 5,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T11:59',
    title: 'Natalie',
    resource: 43,
    slot: 5,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T11:59',
    title: 'Kaylin',
    resource: 44,
    slot: 5,
  },
  {
    start: '2026-10-25T11:00',
    end: '2026-10-25T11:59',
    title: 'Fred',
    resource: 71,
    slot: 5,
  },
  {
    start: '2026-10-26T02:00',
    end: '2026-10-26T10:00',
    title: 'Derek',
    resource: 21,
    slot: 1,
  },
  {
    start: '2026-10-26T02:00',
    end: '2026-10-26T10:00',
    title: 'Florence',
    resource: 46,
    slot: 1,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Willis',
    resource: 12,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Jewell',
    resource: 32,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Jenifer',
    resource: 42,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Jon',
    resource: 52,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Fred',
    resource: 71,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Adrianna',
    resource: 82,
    slot: 2,
  },
  {
    start: '2026-10-26T08:00',
    end: '2026-10-26T12:00',
    title: 'Jude',
    resource: 11,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Jewell',
    resource: 32,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Gray',
    resource: 45,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Isadora',
    resource: 54,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Antonia',
    resource: 72,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Adrianna',
    resource: 82,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Leon',
    resource: 84,
    slot: 3,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T15:00',
    title: 'Yancy',
    resource: 91,
    slot: 3,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Ryan',
    resource: 13,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Merv',
    resource: 22,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Ford',
    resource: 31,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Dory',
    resource: 33,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Breanne',
    resource: 41,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Meredith',
    resource: 51,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Carlyn',
    resource: 53,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Layton',
    resource: 61,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Gerry',
    resource: 73,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Reg',
    resource: 81,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Lou',
    resource: 83,
    slot: 4,
  },
  {
    start: '2026-10-26T16:00',
    end: '2026-10-26T23:00',
    title: 'Sierra',
    resource: 92,
    slot: 4,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T11:59',
    title: 'Derek',
    resource: 21,
    slot: 5,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T11:59',
    title: 'Natalie',
    resource: 43,
    slot: 5,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T11:59',
    title: 'Kaylin',
    resource: 44,
    slot: 5,
  },
  {
    start: '2026-10-26T11:00',
    end: '2026-10-26T11:59',
    title: 'Fred',
    resource: 71,
    slot: 5,
  },
  {
    start: '2026-10-27T02:00',
    end: '2026-10-27T10:00',
    title: 'Theo',
    resource: 23,
    slot: 1,
  },
  {
    start: '2026-10-27T02:00',
    end: '2026-10-27T10:00',
    title: 'Jenifer',
    resource: 42,
    slot: 1,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Jude',
    resource: 11,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Jewell',
    resource: 32,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Florence',
    resource: 46,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Jon',
    resource: 52,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Gerry',
    resource: 73,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Adrianna',
    resource: 82,
    slot: 2,
  },
  {
    start: '2026-10-27T08:00',
    end: '2026-10-27T12:00',
    title: 'Jude',
    resource: 11,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Dory',
    resource: 33,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Natalie',
    resource: 43,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Meredith',
    resource: 51,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Fred',
    resource: 71,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Lou',
    resource: 83,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Leon',
    resource: 84,
    slot: 3,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T15:00',
    title: 'Sierra',
    resource: 92,
    slot: 3,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Willis',
    resource: 12,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Derek',
    resource: 21,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Ford',
    resource: 31,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Dory',
    resource: 33,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Kaylin',
    resource: 44,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Meredith',
    resource: 51,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Isadora',
    resource: 54,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Sylvia',
    resource: 62,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Antonia',
    resource: 72,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Reg',
    resource: 81,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Lou',
    resource: 83,
    slot: 4,
  },
  {
    start: '2026-10-27T16:00',
    end: '2026-10-27T23:00',
    title: 'Sierra',
    resource: 92,
    slot: 4,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T11:59',
    title: 'Derek',
    resource: 21,
    slot: 5,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T11:59',
    title: 'Natalie',
    resource: 43,
    slot: 5,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T11:59',
    title: 'Kaylin',
    resource: 44,
    slot: 5,
  },
  {
    start: '2026-10-27T11:00',
    end: '2026-10-27T11:59',
    title: 'Fred',
    resource: 71,
    slot: 5,
  }, //</hide-comment>
];

var slots = [
  {
    id: 1,
    name: 'Night',
    time: '2AM - 10AM',
  },
  {
    id: 2,
    name: 'Breakfast',
    time: '8AM - 12PM',
  },
  {
    id: 3,
    name: 'Lunch',
    time: '11AM - 3PM',
  },
  {
    id: 4,
    name: 'Dinner',
    time: '4PM - 11PM',
  },
  {
    id: 5,
    name: 'After hours',
    time: '11PM - 1AM',
  },
];

calendar = mobiscroll.eventcalendar('#demo-restaurant-shift-management', {
  view: {
    timeline: {
      type: 'week',
      eventDisplay: 'fill',
      startDay: 1,
      endDay: 5,
    },
  },
  data: data,
  resources: resources,
  slots: slots,
  clickToCreate: true,
  dragToMove: true,
  renderSlot: function (args) {
    var slot = args.slot;
    var date = formatDate(args.date);
    var shiftsElm = '';
    var shiftList = getShiftsNrs(date, slot.id);
    var length = 0;

    if (shiftList.length > 0) {
      for (var i = 0; i < shiftList.length; ++i) {
        if (shiftList[i]) {
          shiftsElm +=
            '<div class="md-shift-count">' +
            '<span class="md-shift-icon">' +
            resources[i].icon +
            '</span>' +
            '<span class="md-shift-nr md-shift-nr-' +
            date +
            '-' +
            slot.id +
            '-' +
            (i + 1) +
            '">' +
            shiftList[i] +
            '</span></div>' +
            (length === 3 ? '<br />' : '');
          ++length;
        }
      }
    } else {
      shiftsElm += '<div class="md-shift-count">😶 empty...</div>';
    }

    return (
      '<div class="md-shift-header">' +
      '<div class="md-shift-name">' +
      slot.name +
      '<span class="md-shift-time">' +
      slot.time +
      '</span></div>' +
      '<div class="md-shift-counts-' +
      date +
      '-' +
      slot.id +
      '">' +
      shiftsElm +
      '</div></div>'
    );
  },
  renderHeader: function () {
    return (
      '<div mbsc-calendar-nav></div>' +
      '<div class="mbsc-flex mbsc-flex-1-0 mbsc-justify-content-end">' +
      '<label>Night' +
      '<input mbsc-checkbox type="checkbox" data-theme="material" class="md-shift-filter" value="1" checked>' +
      '</label>' +
      '<label>Breakfast' +
      '<input mbsc-checkbox type="checkbox" data-theme="material" class="md-shift-filter" value="2" checked>' +
      '</label>' +
      '<label>Lunch' +
      '<input mbsc-checkbox type="checkbox" data-theme="material" class="md-shift-filter" value="3" checked>' +
      '</label>' +
      '<label>Dinner' +
      '<input mbsc-checkbox type="checkbox" data-theme="material" class="md-shift-filter" value="4" checked>' +
      '</label>' +
      '<label>After hours' +
      '<input mbsc-checkbox type="checkbox" data-theme="material" class="md-shift-filter" value="5" checked>' +
      '</label>' +
      '<label>' +
      '<input mbsc-input id="shift-management-view" data-dropdown="true" data-input-style="box" />' +
      '</label>' +
      '<select id="shift-management-select">' +
      '<option value="day">Day</option>' +
      '<option value="week" selected>Week</option>' +
      '</select>' +
      '</div>' +
      '<div mbsc-calendar-prev></div>' +
      '<div mbsc-calendar-today></div>' +
      '<div mbsc-calendar-next></div>'
    );
  },
  renderResource: function (resource) {
    var parent = resource.children;
    return (
      '<div class="' +
      (parent ? 'md-shift-resource' : '') +
      '" style="color:' +
      (parent ? parent[0].color : '') +
      '">' +
      (parent ? '<span class="md-shift-resource-icon">' + resource.icon + '</span>' : '') +
      resource.name +
      '</div>'
    );
  },
  extendDefaultEvent: function (event) {
    return {
      title: getEmployeeName(event),
    };
  },
  onEventCreate: function (args, inst) {
    return addUpdateEvent(args.event, inst);
  },
  onEventUpdate: function (args, inst) {
    var event = Object.assign({}, args.event);
    if (addUpdateEvent(event, inst)) {
      event.title = getEmployeeName(event);
      if (event.title) {
        inst.updateEvent(event);
      }
    } else {
      return false;
    }
  },
});

function addUpdateEvent(event, inst) {
  var date = event.start.setHours(0);
  var events = inst.getEvents(date);
  var ev = events.find(function (e) {
    return new Date(e.start).setHours(0) === date && e.resource === event.resource && e.slot === event.slot;
  });
  return ev ? false : true;
}

function getEmployeeName(event) {
  for (var i = 0; i < resources.length; ++i) {
    for (var j = 0; j < resources[i].children.length; ++j) {
      var employee = resources[i].children[j];
      if (employee.id === event.resource) {
        return employee.name.substring(0, employee.name.indexOf(' '));
      }
    }
  }
}

function getShiftsNrs(date, slotId) {
  var shifts = calendar ? calendar.getEvents(date) : data;
  var shiftNrs = [];

  for (var i = 0; i < shifts.length; ++i) {
    var shift = shifts[i];
    // Get slot id from resource id
    var resourceNr = +shift.resource.toString().charAt(0);
    if (shift.slot == slotId && date == formatDate(shift.start)) {
      shiftNrs[resourceNr - 1] = (shiftNrs[resourceNr - 1] || 0) + 1;
    }
  }
  return shiftNrs;
}

function formatDate(date) {
  return mobiscroll.formatDate('YYYY-MM-DD', new Date(date));
}

mobiscroll.select('#shift-management-select', {
  inputElement: document.getElementById('shift-management-view'),
  touchUi: false,
  onChange: function (args) {
    if (args.value == 'day') {
      calendar.setOptions({
        view: {
          timeline: {
            type: 'day',
            eventDisplay: 'fill',
          },
        },
      });
    } else {
      calendar.setOptions({
        view: {
          timeline: {
            type: 'week',
            eventDisplay: 'fill',
            startDay: 1,
            endDay: 5,
          },
        },
      });
    }
  },
});

document.querySelectorAll('.md-shift-filter').forEach(function (elm) {
  elm.addEventListener('change', function () {
    var selected = [];
    var displayedSlots = [];

    document.querySelectorAll('.md-shift-filter:checked').forEach(function (el) {
      selected.push(+el.value);
      mobiscroll.getInst(el).setOptions({ disabled: false });
    });

    for (var i = 0; i < slots.length; ++i) {
      var slot = slots[i];

      if (selected.indexOf(slot.id) > -1) {
        displayedSlots.push(slot);
      }
    }

    if (selected.length === 1) {
      var lastElm = document.querySelectorAll('.md-shift-filter[value="' + selected[0] + '"]')[0];
      mobiscroll.getInst(lastElm).setOptions({ disabled: true });
    }

    calendar.setOptions({ slots: displayedSlots });
  });
});
```

### HTML

```html
<div id="demo-restaurant-shift-management" class="md-shift-management-calendar"></div>
```

### CSS

```css
.md-shift-management-calendar .mbsc-schedule-event {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 36px;
}

.md-shift-management-calendar .mbsc-timeline-slots {
    height: 94px;
}

.md-shift-management-calendar .mbsc-timeline-header-placeholder {
    height: 120px;
}

.md-shift-management-calendar .mbsc-timeline-resource {
    display: flex;
    align-items: center;
}

.md-shift-management-calendar .mbsc-timeline-slot {
    width: 200px;
}

.md-shift-management-calendar .mbsc-timeline-parent {
    height: 40px;
}

.md-shift-management-calendar .mbsc-checkbox {
    padding: 18px 0 18px 45px;
}

.md-shift-management-calendar .mbsc-textfield-wrapper {
    margin: 12px 12px 12px 28px;
}

.md-shift-management-calendar .mbsc-select.mbsc-textfield {
    height: 32px;
    width: 120px;
}

.md-shift-management-calendar .mbsc-icon.mbsc-select-icon {
    width: 20px;
    height: 20px;
    top: 7px;
}

.md-shift-resource {
    font-size: 18px;
}

.md-shift-resource-icon {
    margin-right: 10px;
}

.md-shift-header {
    margin: 3px 6px;
    display: flex;
    flex-direction: column;
}

.md-shift-name {
    font-size: 15px;
}

.md-shift-time {
    opacity: .5;
    font-size: 12px;
    margin-left: 5px;
}

.md-shift-count {
    margin-top: 6px;
    margin-right: 12px;
    display: inline-block;
    width: 26px;
    height: 20px;
}

.md-shift-icon {
    padding-right: 3px;
}
```

## Implementation notes

- Use the timeline view in summary mode with `eventDisplay: 'fill'`. For the week view, set `startDay: 1` and `endDay: 5` to show only working days; support switching to `type: 'day'` via a `Select` control in the header.
- Define the named shift buckets with the `slots` option. Each slot object needs an `id`, a `name`, and a custom `time` string (e.g. `"8AM – 12PM"`) for display. Each event references its slot via the `slot` field.
- Structure resources as a two-level tree: role groups (Barista, Bartenders, Chefs, etc.) at the top level with individual employee leaf nodes beneath them. Set `eventCreation: false` on every group node so shifts are only created on employee rows. Give each employee a `color` property that Mobiscroll automatically applies to their events.
- Use `renderResource` (Angular: `resourceTemplate`, Vue: `#resource` slot) to differentiate group and leaf rows - show the role icon and a colored label for group nodes, and just the employee name for leaf nodes.
- Use `renderSlot` (Angular: `slotTemplate`, Vue: `#slot` slot) to render a custom slot column header showing the slot name and time range, followed by a live occupancy count per role category (icon + number of assigned employees) for that slot and date. Show an "empty..." fallback when no shifts are assigned.
- Calculate occupancy counts by iterating over the current events array and counting how many employees per role category have a shift matching the given slot ID and date string.
- Use `renderHeader` (Angular: `headerTemplate`, Vue: `#header` slot) to render a custom toolbar combining `CalendarNav`/`CalendarPrev`/`CalendarToday`/`CalendarNext` navigation controls with `Checkbox` components for toggling each slot's visibility and a `Select` for switching between day and week views.
- Toggle slot visibility by maintaining a filtered `slots` array in state: uncheck a checkbox to remove the slot, check it to add it back. When only one slot remains checked, disable its checkbox so at least one slot is always visible.
- Enable `clickToCreate` so users can sign an employee up for a shift by clicking an empty cell. Set `dragToMove: true` to allow repositioning shifts by dragging.
- Use `extendDefaultEvent` to pre-populate the newly created event's `title` with the employee's first name, looked up from the resource data using the event's `resource` ID.
- Use `onEventCreate` to validate new shifts before saving: call `inst.getEvents()` to check whether the same resource already has a shift in the same slot on the same day, and return `false` to cancel the creation if a duplicate is found.
- Use `onEventUpdate` to apply the same duplicate check after a drag; also update the event `title` when the resource changes so it reflects the new employee's name. Return `false` to revert if the target cell is already occupied.
- Use `onEventDeleted` to remove the deleted shift from the events array in state.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-javascript.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config javascript
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config javascript --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/javascript/getting-started/installation

## Related demos

- Previous: [Timezone Meeting Planner](https://demo.mobiscroll.com/javascript/timeline/timezone-meeting-planner.md) - Plan team meetings across time zones with the Javascript Timeline — show per-resource availability in working, flex, and time-off blocks, with local-time events.
- Next: [Meal Planner](https://demo.mobiscroll.com/javascript/timeline/meal-planner.md) - Build a weekly meal planner with the Javascript Timeline — meal types as color-coded resource rows, custom add/edit popover for creating or updating meals.
