Overview

Apply a complex filter to rooms and reservations displayed using JavaScript Scheduler component. The filter has two parameters:

  • Select the target date range using drag and drop

  • Select room size using a drop down list

If you select a date range, only rooms that are available between these days will be displayed.

This feature can be used in the hotel room reservation tutorials, which are available for ASP.NET Core [Tutorial: ASP.NET Core Hotel Room Booking App) and PHP [PHP Hotel Room Booking System (JavaScript/HTML5, MySQL)].

The project includes a trial version of DayPilot Pro for JavaScript (see License below).

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.

Filter Available Rooms by Date

We will keep the room filter parameters in a global app.filter object. The object has two properties (range, size).

  • The filter.range property will hold the selected date range.

  • The filter.size property will hold the requested room size.

const app = {
  filter: {
    range: null,
    size: null
  },
  // ...
};

Users of our application will be able to select a date range using the Navigator date picker component. The date picker lets them select a date range unit defined by selectMode property (day/week/month). In addition to that, we will also enable a free-hand selection of a custom date range using freeHandSelectionEnabled property.

javascript html5 scheduler room filtering date picker

The Scheduler will use the dates to check availability of each room and hide rooms that are not available.

const datePicker = new DayPilot.Navigator("nav", {
  freeHandSelectionEnabled: true,
  onTimeRangeSelected: (args) => {
    app.filter.range = {
      start: args.start,
      end: args.end
    };
    app.applyRoomFilter();
  }
});
datePicker.init();

The onTimeRangeSelected event handler of the DayPilot.Navigator object is fired when the user selects a date range. It saves the selected dates in range property of the filter object and calls applyRoomFilter() to update the JavaScript Scheduler component.

Note that the freeHandSelectionEnabled property allows selecting custom date range using drag and drop in the Navigator.

javascript html5 scheduler room filtering label

The applyRoomFilter() method calls rows.filter() method to apply the filter to the Scheduler component and displays a label with the selected dates:

const app = {

  applyRoomFilter() {
    if (app.filter.range) {
      app.elements.filterDate.style.display = "inline-block";
      app.elements.filterDateString.innerText = app.filter.range.start.toString("M/d/yyyy") + " - " + app.filter.range.end.addDays(-1).toString("M/d/yyyy");
    }
    else {
      app.elements.filterDate.style.display = "none";
    }
    scheduler.rows.filter(app.filter);
  },

  // ...

};

Filter Rooms by Size

javascript html5 scheduler room filtering size

The room size filter is created using a simple <select> element:

<select id="filter-size">
  <option value="0">All rooms</option>
  <option value="2">2+ beds</option>
  <option value="3">3+ beds</option>
  <option value="4">4+ beds</option>
</select>

We will watch for changes of the selected value and store the filter value in filter.size. The applyRoomFilter() call updates the Scheduler.

app.elements.filterSize.addEventListener("change", function(ev) {
  filter.size = elements.filterSize.value;
  applyRoomFilter();
});

Room Filtering Logic

Whenever the rows.filter() method is called from applyRoomFilter() the Scheduler updates itself and calls onRowFilter event handler for each room (row) to determine the visibility. See also row filtering [doc.daypilot.org].

Each condition is tested separately and the row is only marked visible if both of them are met.

  • The dateMatches value is calculated by checking the if there are any events (reservations). The args.row.events.forRange() method returns all reservations that overlap the specified range. The room is available if there are no reservations found.

  • The sizeMatches value is calculated by comparing the size filter parameter with room size stored in resources.

Note: In this case, we access the filter parameters using the global variable (filter). The applyRoomFilter() method uses the filter as a parameter for rows.filter() call. That makes it available as args.filter inside onRowFilter event handler.

const scheduler = new DayPilot.Scheduler("dp", {

  onRowFilter: (args) => {
    const sizeMatches = args.row.data.size >= app.filter.size;
    const dateMatches = !app.filter.range || args.row.events.forRange(app.filter.range.start, app.filter.range.end).length === 0;

    args.visible = sizeMatches && dateMatches;
  },
  
  // ...
  
});

The room sizes are stored in resources array as an additional property:

dp.resources = [
  {name: "Room 101", id: "R101", size: 1},
  {name: "Room 102", id: "R102", size: 3},
  {name: "Room 103", id: "R103", size: 2},
  {name: "Room 104", id: "R104", size: 4},
  {name: "Room 105", id: "R105", size: 2},
  {name: "Room 106", id: "R106", size: 2},
  {name: "Room 107", id: "R107", size: 2},
  {name: "Room 108", id: "R108", size: 2},
  {name: "Room 109", id: "R109", size: 4},
  {name: "Room 110", id: "R110", size: 3},
  {name: "Room 111", id: "R111", size: 2},
  {name: "Room 112", id: "R112", size: 2},
];

Highlighting the Date Range in the Scheduler

javascript html5 scheduler component room filtering availability highlighting

The selected range is highlighted in the time header using onBeforeTimeHeaderRender event handler:

const scheduler = new DayPilot.Scheduler("dp", {

  onBeforeTimeHeaderRender: (args) => {
    if (app.filter.range) {
      if (args.header.level === 1 && DayPilot.Util.overlaps(app.filter.range.start, app.filter.range.end, args.header.start, args.header.end)) {
        args.header.backColor = "#6aa84f";
        args.header.fontColor = "#fff";
      }
    }
  },
  
  // ...

});

The Scheduler grid cells are highlighted using onBeforeCellRender event handler:

const scheduler = new DayPilot.Scheduler("dp", {

  onBeforeCellRender: (args) => {
    if (app.filter.range) {
      if (DayPilot.Util.overlaps(args.cell.start, args.cell.end, app.filter.range.start, app.filter.range.end)) {
        args.cell.backColor = "#b6d7a8";
      }
    }
  },
  
  // ...

});

Both event handlers access the filter global variable that stores the filter parameters.