{"id":11747,"date":"2023-09-26T12:30:38","date_gmt":"2023-09-26T09:30:38","guid":{"rendered":"https:\/\/dhtmlx.com\/blog\/?p=11747"},"modified":"2026-09-24T18:27:25","modified_gmt":"2026-09-24T15:27:25","slug":"hotel-booking-calendar-dhtmlx-scheduler-angular","status":"publish","type":"post","link":"https:\/\/dhtmlx.com\/blog\/hotel-booking-calendar-dhtmlx-scheduler-angular\/","title":{"rendered":"How to Make Hotel Booking Calendar Using DHTMLX Scheduler and Angular"},"content":{"rendered":"<p>Welcome to this tutorial, where we bring together two powerful tools: the <a href=\"https:\/\/dhtmlx.com\/docs\/products\/dhtmlxScheduler\/\" rel=\"noopener\" target=\"_blank\">DHTMLX Scheduler<\/a> library and the <a href=\"https:\/\/angular.dev\/\" rel=\"noopener\" target=\"_blank\">Angular<\/a> framework, to create a comprehensive hotel room booking application.<\/p>\n<p>In this post our goal is to create an application that will look exactly like this:<br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1.jpg\" alt=\"Angular Hotel Booking App\" width=\"957\" height=\"744\" class=\"aligncenter size-full wp-image-11748\" srcset=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1.jpg 957w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1-300x233.jpg 300w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1-768x597.jpg 768w\" sizes=\"auto, (max-width: 957px) 100vw, 957px\" \/><\/p>\n<p>Our Angular hotel booking app will be able to display hotel rooms, room types, and room status, reservations made for particular dates and booking status. The app will also allow performing CRUD operations.<\/p>\n<p>If you&#8217;re new to configuring DHTMLX Scheduler for room reservation purposes or integrating it into Angular applications, don&#8217;t worry. We&#8217;ve got you covered with dedicated tutorials for both:<\/p>\n<ul>\n<li><a href=\"https:\/\/dhtmlx.com\/blog\/how-to-make-hotel-booking-calendar\/\" rel=\"noopener\" target=\"_blank\">Configuring DHTMLX Scheduler for Hotel Booking<\/a><\/li>\n<li><a href=\"https:\/\/dhtmlx.com\/blog\/angular-dhtmlxscheduler-tutorial\/\" rel=\"noopener\" target=\"_blank\">Integrating DHTMLX Scheduler into an Angular App<\/a><\/li>\n<\/ul>\n<p>We also guide you through the process of setting up a Node.js server. You can find a separate tutorial devoted to working with Scheduler and Node.js <a href=\"https:\/\/docs.dhtmlx.com\/scheduler\/howtostart_nodejs.html\" rel=\"noopener\" target=\"_blank\">in the documentation<\/a>.<\/p>\n<p>You can find the full source code of the Angular Hotel Room Reservation demo <a href=\"https:\/\/github.com\/DHTMLX\/angular-scheduler-hotel-booking\" rel=\"noopener\" target=\"_blank\">in our GitHub repository<\/a>.<\/p>\n<p>And here is the video tutorial with all the steps, which you can follow to get the result faster:<\/p>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/23m7xUYMOqs?si=BH7ldZcOrshw0VT2\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe><\/p>\n<p>Let&#8217;s dive in!<\/p>\n<h5>Step 0 \u2013 Prerequisites<\/h5>\n<p>Before we begin, make sure you have <a href=\"https:\/\/nodejs.org\/en\" rel=\"noopener\" target=\"_blank\">Node.js<\/a> and <a href=\"https:\/\/v17.angular.io\/cli\" rel=\"noopener\" target=\"_blank\">Angular CLI<\/a>.<\/p>\n<h5>Step 1 \u2013 Preparing App<\/h5>\n<p>To create an app skeleton, run the following command:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">ng new room-reservation-angular<\/div><\/div>\n<p>After the operation finishes, we can go to the app directory and run the application:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">cd room-reservation-angular<br \/>\nng serve<\/div><\/div>\n<p>Now if we open <em>http:\/\/127.0.0.1:4200<\/em> we should see the initial page. The <strong>ng serve<\/strong> command will watch the source file and, if necessary, will change and rebuild the app.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Angular-Booking-App-Step-1.png\" alt=\"Angular Booking App - Step 1\" width=\"958\" height=\"967\" class=\"aligncenter size-full wp-image-11751\" srcset=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Angular-Booking-App-Step-1.png 958w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Angular-Booking-App-Step-1-150x150.png 150w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Angular-Booking-App-Step-1-297x300.png 297w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Angular-Booking-App-Step-1-768x775.png 768w\" sizes=\"auto, (max-width: 958px) 100vw, 958px\" \/><\/p>\n<h5>Step 2 \u2013 Create the Data Models<\/h5>\n<p>Let&#8217;s define the <strong>Reservation<\/strong>, <strong>Room<\/strong>, <strong>RoomType<\/strong>, <strong>CleaningStatus<\/strong>, and <strong>BookingStatus<\/strong> models. Run the following commands:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">ng generate interface models\/reservation model<br \/>\nng generate interface models\/room model<br \/>\nng generate interface models\/roomType model<br \/>\nng generate interface models\/cleaningStatus model<br \/>\nng generate interface models\/bookingStatus model<\/div><\/div>\n<p>In the newly created <em>reservation.model.ts<\/em> file inside the <em>models<\/em> folder, we will add the following code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">export interface Reservation {<br \/>\n&nbsp; &nbsp; id: number;<br \/>\n&nbsp; &nbsp; start_date: string;<br \/>\n&nbsp; &nbsp; end_date: string;<br \/>\n&nbsp; &nbsp; text: string;<br \/>\n&nbsp; &nbsp; room: string;<br \/>\n&nbsp; &nbsp; booking_status: string;<br \/>\n&nbsp; &nbsp; is_paid: string;<br \/>\n}<\/div><\/div>\n<p>In the <em>room.model.ts<\/em>, <em>room-type.model.ts<\/em>, <em>cleaning-status.model.ts<\/em>,  <em>booking-status.model.ts files<\/em>, add the next lines of code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">export interface Room {<br \/>\n&nbsp; &nbsp; id: number;<br \/>\n&nbsp; &nbsp; value: string;<br \/>\n&nbsp; &nbsp; label: string;<br \/>\n&nbsp; &nbsp; type: string;<br \/>\n&nbsp; &nbsp; cleaning_status: string;<br \/>\n}<br \/>\n<br \/>\n<br \/>\nexport interface RoomType {<br \/>\n&nbsp; &nbsp; id: string;<br \/>\n&nbsp; &nbsp; value: string;<br \/>\n&nbsp; &nbsp; label: string;<br \/>\n}<br \/>\n<br \/>\n<br \/>\nexport interface CleaningStatus {<br \/>\n&nbsp; &nbsp; id: string;<br \/>\n&nbsp; &nbsp; value: string;<br \/>\n&nbsp; &nbsp; label: string;<br \/>\n&nbsp; &nbsp; color: string;<br \/>\n}<br \/>\n<br \/>\n<br \/>\nexport interface BookingStatus {<br \/>\n&nbsp; &nbsp; id: string;<br \/>\n&nbsp; &nbsp; value: string;<br \/>\n&nbsp; &nbsp; label: string;<br \/>\n}<\/div><\/div>\n<h5>Step 3 \u2013 Creating Scheduler Component<\/h5>\n<p>Download the latest <a href=\"https:\/\/dhtmlx.com\/docs\/products\/dhtmlxScheduler\/download.shtml\" rel=\"noopener\" target=\"_blank\">free 30-day trial of the PRO edition of DHTMLX Scheduler<\/a>. Extract the downloaded package to your local machine to the root folder of your project. You can read the article <a href=\"https:\/\/docs.dhtmlx.com\/scheduler\/install_with_bower.html#addingproeditionintoproject\" rel=\"noopener\" target=\"_blank\">&#8220;Adding PRO Edition into Project&#8221;<\/a> for more details.<br \/>\nTo be able to embed Scheduler into the app, you should get the DHTMLX Scheduler code. Run the following command:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">npm install .\/scheduler_6.0.5_trial<\/div><\/div>\n<p>Create a new component. For this, run the following command:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">ng generate component scheduler --skip-tests<\/div><\/div>\n<p>The newly created <em>scheduler.component.html<\/em> file inside the <em>scheduler<\/em> folder will contain the <a href=\"https:\/\/docs.dhtmlx.com\/scheduler\/scheduler_markup.html\" rel=\"noopener\" target=\"_blank\">template for the scheduler<\/a>. Let&#8217;s add the next lines of code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">&lt;div #scheduler_here class='dhx_cal_container' style='width:100%; height:100vh'&gt;<br \/>\n&nbsp; &nbsp; &lt;div class='dhx_cal_navline'&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;div style='font-size:16px;padding:4px 20px;'&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Show rooms:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;select id='room_filter' [(ngModel)]='selectedRoomType' (ngModelChange)='filterRoomsByType($event)'&gt;&lt;\/select&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class='dhx_cal_prev_button'&gt;&amp;nbsp;&lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class='dhx_cal_next_button'&gt;&amp;nbsp;&lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class='dhx_cal_today_button'&gt;&lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class='dhx_cal_date'&gt;&lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &lt;div class='dhx_cal_header'&gt;&lt;\/div&gt;<br \/>\n&nbsp; &nbsp; &lt;div class='dhx_cal_data'&gt;&lt;\/div&gt;<br \/>\n&lt;\/div&gt;<\/div><\/div>\n<p>We utilized the <strong>ngModel<\/strong> and <strong>ngModelChange<\/strong> directives to establish interaction between the <strong>select<\/strong> element and data within the component. Please, add the <strong>FormsModule<\/strong> module to the <em>app.module.ts<\/em> file.<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { NgModule } from '@angular\/core';<br \/>\nimport { BrowserModule } from '@angular\/platform-browser';<br \/>\n<br \/>\n<br \/>\nimport { AppRoutingModule } from '.\/app-routing.module';<br \/>\nimport { AppComponent } from '.\/app.component';<br \/>\nimport { SchedulerComponent } from '.\/scheduler\/scheduler.component';<br \/>\n<br \/>\n<br \/>\nimport { FormsModule } from '@angular\/forms';<br \/>\n<br \/>\n<br \/>\n@NgModule({<br \/>\n&nbsp; &nbsp; declarations: [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; AppComponent,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; SchedulerComponent<br \/>\n&nbsp; &nbsp; ],<br \/>\n&nbsp; &nbsp; imports: [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; BrowserModule,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; AppRoutingModule,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; FormsModule<br \/>\n&nbsp; &nbsp; ],<br \/>\n&nbsp; &nbsp; providers: [],<br \/>\n&nbsp; &nbsp; bootstrap: [AppComponent]<br \/>\n})<br \/>\nexport class AppModule { }<\/div><\/div>\n<p>We&#8217;ll declare scheduler styles in the separate file named <em>scheduler.component.css<\/em>. Styles can look in the following way:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">@import '~dhtmlx-scheduler\/codebase\/dhtmlxscheduler_flat.css';<br \/>\n:host {<br \/>\n&nbsp; &nbsp; display: block;<br \/>\n&nbsp; &nbsp; position: relative;<br \/>\n&nbsp; &nbsp; height: 100%;<br \/>\n&nbsp; &nbsp; width: 100%;<br \/>\n}<br \/>\nhtml, body {<br \/>\n&nbsp; &nbsp; margin: 0;<br \/>\n&nbsp; &nbsp; padding: 0;<br \/>\n&nbsp; &nbsp; height: 100%;<br \/>\n&nbsp; &nbsp; overflow: hidden;<br \/>\n}<br \/>\n.dhx_cal_container #room_filter:focus {<br \/>\n&nbsp; &nbsp; outline: 1px solid #52daff;<br \/>\n}<br \/>\n.timeline-cell-inner {<br \/>\n&nbsp; &nbsp; height: 100%;<br \/>\n&nbsp; &nbsp; width: 100%;<br \/>\n&nbsp; &nbsp; table-layout: fixed;<br \/>\n}<br \/>\n.timeline-cell-inner td {<br \/>\n&nbsp; &nbsp; border-left: 1px solid #cecece;<br \/>\n}<br \/>\n.dhx_section_time select {<br \/>\n&nbsp; &nbsp; display: none;<br \/>\n}<br \/>\n.timeline_weekend {<br \/>\n&nbsp; &nbsp; background-color: #FFF9C4;<br \/>\n}<br \/>\n.timeline_item_cell {<br \/>\n&nbsp; &nbsp; width: 32%;<br \/>\n&nbsp; &nbsp; height: 100% !important;<br \/>\n&nbsp; &nbsp; font-size: 14px;<br \/>\n&nbsp; &nbsp; text-align: center;<br \/>\n&nbsp; &nbsp; line-height: 50px;<br \/>\n}<br \/>\n.cleaning_status {<br \/>\n&nbsp; &nbsp; position: relative;<br \/>\n}<br \/>\n.timeline_item_separator {<br \/>\n&nbsp; &nbsp; background-color: #CECECE;<br \/>\n&nbsp; &nbsp; width: 1px;<br \/>\n&nbsp; &nbsp; height: 100% !important;<br \/>\n}<br \/>\n.dhx_cal_event_line {<br \/>\n&nbsp; &nbsp; background-color: #FFB74D !important;<br \/>\n}<br \/>\n.event_1 {<br \/>\n&nbsp; &nbsp; background-color: #FFB74D !important;<br \/>\n}<br \/>\n.event_2 {<br \/>\n&nbsp; &nbsp; background-color: #9CCC65 !important;<br \/>\n}<br \/>\n.event_3 {<br \/>\n&nbsp; &nbsp; background-color: #40C4FF !important;<br \/>\n}<br \/>\n.event_4 {<br \/>\n&nbsp; &nbsp; background-color: #BDBDBD !important;<br \/>\n}<br \/>\n.booking_status,<br \/>\n.booking_paid {<br \/>\n&nbsp; &nbsp; position: absolute;<br \/>\n&nbsp; &nbsp; right: 5px;<br \/>\n}<br \/>\n.booking_status {<br \/>\n&nbsp; &nbsp; top: 2px;<br \/>\n}<br \/>\n.booking_paid {<br \/>\n&nbsp; &nbsp; bottom: 2px;<br \/>\n}<br \/>\n.dhx_cal_event_line:hover .booking-option {<br \/>\n&nbsp; &nbsp; background: none !important;<br \/>\n}<br \/>\n.dhx_cal_header .dhx_scale_bar {<br \/>\n&nbsp; &nbsp; line-height: 26px;<br \/>\n&nbsp; &nbsp; color: black;<br \/>\n}<br \/>\n.dhx_section_time select {<br \/>\n&nbsp; &nbsp; display: none<br \/>\n}<br \/>\n.dhx_mini_calendar .dhx_year_week,<br \/>\n.dhx_mini_calendar .dhx_scale_bar {<br \/>\n&nbsp; &nbsp; height: 30px !important;<br \/>\n}<br \/>\n.dhx_cal_light_wide .dhx_section_time {<br \/>\n&nbsp; &nbsp; text-align: left;<br \/>\n}<br \/>\n.dhx_cal_light_wide .dhx_section_time &gt; input:first-child {<br \/>\n&nbsp; &nbsp; margin-left: 10px;<br \/>\n}<br \/>\n.dhx_cal_light_wide .dhx_section_time input {<br \/>\n&nbsp; &nbsp; border: 1px solid #aeaeae;<br \/>\n&nbsp; &nbsp; padding-left: 5px;<br \/>\n}<br \/>\n.dhx_cal_light_wide .dhx_readonly {<br \/>\n&nbsp; &nbsp; padding: 3px;<br \/>\n}<br \/>\n.collection_label .timeline_item_cell {<br \/>\n&nbsp; &nbsp; line-height: 60px;<br \/>\n}<br \/>\n.dhx_cal_radio label,<br \/>\n.dhx_cal_radio input {<br \/>\n&nbsp; &nbsp; vertical-align: middle;<br \/>\n}<br \/>\n.dhx_cal_radio input {<br \/>\n&nbsp; &nbsp; margin-left: 10px;<br \/>\n&nbsp; &nbsp; margin-right: 2px;<br \/>\n}<br \/>\n.dhx_cal_radio input:first-child {<br \/>\n&nbsp; &nbsp; margin-left: 5px;<br \/>\n}<br \/>\n.dhx_cal_radio {<br \/>\n&nbsp; &nbsp; line-height: 19px;<br \/>\n}<br \/>\n.dhtmlXTooltip.tooltip {<br \/>\n&nbsp; &nbsp; color: #4d4d4d;<br \/>\n&nbsp; &nbsp; font-size: 15px;<br \/>\n&nbsp; &nbsp; line-height: 140%;<br \/>\n}<\/div><\/div>\n<p>To make the scheduler container occupy the entire space of the body, you need to add the following styles to the <em>styles.css<\/em> file located in the <em>src<\/em> folder:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">body,<br \/>\nhtml {<br \/>\n&nbsp; &nbsp;width: 100%;<br \/>\n&nbsp; &nbsp;height: 100%;<br \/>\n&nbsp; &nbsp;margin: unset;<br \/>\n}<\/div><\/div>\n<p>To proceed, we need to import the required modules and add the necessary code lines to the <em>scheduler.component.ts<\/em> file:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular\/core';<br \/>\nimport { Scheduler } from 'dhtmlx-scheduler';<br \/>\nimport { Reservation } from &quot;..\/models\/reservation.model&quot;;<br \/>\nimport { Room } from &quot;..\/models\/room.model&quot;;<br \/>\n<br \/>\n<br \/>\n@Component({<br \/>\n&nbsp; &nbsp; encapsulation: ViewEncapsulation.None,<br \/>\n&nbsp; &nbsp; selector: 'scheduler',<br \/>\n&nbsp; &nbsp; styleUrls: ['.\/scheduler.component.css'],<br \/>\n&nbsp; &nbsp; templateUrl: '.\/scheduler.component.html'<br \/>\n})<br \/>\n<br \/>\n<br \/>\nexport class SchedulerComponent implements OnInit {<br \/>\n&nbsp; &nbsp; @ViewChild('scheduler_here', {static: true}) schedulerContainer!: ElementRef;<br \/>\n&nbsp; &nbsp; private scheduler: any;<br \/>\n&nbsp; &nbsp; rooms: any[] = [];<br \/>\n&nbsp; &nbsp; roomTypes: any[] = [];<br \/>\n&nbsp; &nbsp; cleaningStatuses: any[] = [];<br \/>\n&nbsp; &nbsp; bookingStatuses: any[] = [];<br \/>\n&nbsp; &nbsp; selectedRoomType: string = '';<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; public filterRoomsByType(value: string): void {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const currentRooms = value === 'all' ? this.rooms.slice() : this.rooms.filter(room =&gt; room.type === value);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.scheduler.updateCollection('currentRooms', currentRooms);<br \/>\n&nbsp; &nbsp; };<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; ngOnInit() {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const scheduler = Scheduler.getSchedulerInstance();<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.scheduler = scheduler;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.selectedRoomType = 'all';<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.plugins({<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; limit: true,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; collision: true,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; timeline: true,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; editors: true,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; minical: true,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tooltip: true<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels['section_text'] = 'Name';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels['section_room'] = 'Room';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels['section_booking_status'] = 'Booking Status';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels['section_is_paid'] = 'Paid';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels.section_time = 'Time';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.xy.scale_height = 30;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.details_on_create = true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.details_on_dblclick = true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.prevent_cache = true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.show_loading = true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.date_format = '%Y-%m-%d %H:%i';<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.rooms = scheduler.serverList('rooms');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.roomTypes = scheduler.serverList('roomTypes');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.cleaningStatuses = scheduler.serverList('cleaningStatuses');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.bookingStatuses = scheduler.serverList('bookingStatuses');<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.config.lightbox.sections = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { name: 'text', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; map_to: 'text', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'textarea', height: 24 },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { name: 'room', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; map_to: 'room', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'select', options: scheduler.serverList(&quot;currentRooms&quot;) },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { name: 'booking_status', map_to: 'booking_status', type: 'radio', options: scheduler.serverList('bookingStatuses') },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { name: 'is_paid', &nbsp; &nbsp; &nbsp; &nbsp;map_to: 'is_paid', &nbsp; &nbsp; &nbsp; &nbsp;type: 'checkbox', checked_value: true, unchecked_value: false },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { name: 'time', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; map_to: 'time', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'calendar_time' }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.locale.labels['timeline_tab'] = 'Timeline';<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.createTimelineView({<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'timeline',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; y_property: 'room',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; render: 'bar',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x_unit: 'day',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x_date: '%d',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dy: 52,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; event_dy: 48,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; section_autoheight: false,<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; y_unit: scheduler.serverList('currentRooms'),<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; second_scale: {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x_unit: 'month',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x_date: '%F %Y'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; columns: [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { label: 'Room', width: 70, template: (room: Room) =&gt; room.label },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { label: 'Type', width: 90, template: (room: Room) =&gt; this.getRoomType(room.type) },<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { label: 'Status', width: 90, template: this.generateCleaningStatusColumnTemplate.bind(this) }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.schedulerContainer.nativeElement.addEventListener('input', (event: any) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const target = &nbsp;event.target as HTMLSelectElement;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (target instanceof HTMLElement &amp;&amp; target.closest('.cleaning-status-select')) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.handleCleaningStatusChange(target);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.date['timeline_start'] = scheduler.date.month_start;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.date['add_timeline'] = (date: any, step: any) =&gt; scheduler.date.add(date, step, 'month');<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onBeforeViewChange', (old_mode, old_date, mode, date) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const year = date.getFullYear();<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const month = (date.getMonth() + 1);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const d = new Date(year, month, 0);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const daysInMonth = d.getDate();<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scheduler.matrix['timeline'].x_size = daysInMonth;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.templates.event_class = (start, end, event) =&gt; 'event_' + (event.booking_status || '');<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; function getPaidStatus(isPaid: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return isPaid ? 'paid' : 'not paid';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const eventDateFormat = scheduler.date.date_to_str('%d %M %Y');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.templates.event_bar_text = (start, end, event) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const paidStatus = getPaidStatus(event.is_paid);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const startDate = eventDateFormat(event.start_date);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const endDate = eventDateFormat(event.end_date);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return [event.text + '&lt;br \/&gt;',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; startDate + ' - ' + endDate,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div class='booking_status booking-option'&gt;${this.getBookingStatus(event.booking_status)}&lt;\/div&gt;`,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div class='booking_paid booking-option'&gt;${paidStatus}&lt;\/div&gt;`].join('');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; };<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.templates.tooltip_text = (start, end, event) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const room = this.getRoom(event.room) || {label: ''};<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const html = [];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; html.push('Booking: &lt;b&gt;' + event.text + '&lt;\/b&gt;');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; html.push('Room: &lt;b&gt;' + room.label + '&lt;\/b&gt;');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; html.push('Check-in: &lt;b&gt;' + eventDateFormat(start) + '&lt;\/b&gt;');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; html.push('Check-out: &lt;b&gt;' + eventDateFormat(end) + '&lt;\/b&gt;');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; html.push(this.getBookingStatus(event.booking_status) + ', ' + getPaidStatus(event.is_paid));<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return html.join('&lt;br&gt;')<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; };<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.templates.lightbox_header = (start, end, ev) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const formatFunc = scheduler.date.date_to_str('%d.%m.%Y');<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return formatFunc(start) + ' - ' + formatFunc(end);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; };<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onEventCollision', (ev, evs) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (let i = 0; i &lt; evs.length; i++) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ev.room != evs[i].room) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scheduler.message({<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'error',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text: 'This room is already booked for this date.'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onEventCreated', (event_id) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const ev = scheduler.getEvent(event_id);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ev.booking_status = 1;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ev.is_paid = false;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ev.text = 'new booking';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.addMarkedTimespan({days: [0, 6], zones: 'fullday', css: 'timeline_weekend'});<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; function setHourToNoon(event: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; event.start_date.setHours(12, 0, 0);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; event.end_date.setHours(12, 0, 0);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onEventLoading', (ev) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.filterRoomsByType('all');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const select = document.getElementById('room_filter') as HTMLSelectElement;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (select !== null) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const selectHTML = [`&lt;option value='all'&gt;All&lt;\/option&gt;`];<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (let i = 1; i &lt; this.roomTypes.length + 1; i++) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const roomType = this.roomTypes[i-1];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selectHTML.push(`&lt;option value='${roomType.key}'&gt;${this.getRoomType(roomType.key)}&lt;\/option&gt;`);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select.innerHTML = selectHTML.join('');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setHourToNoon(ev);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onEventSave', (id, ev, is_new) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!ev.text) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scheduler.alert('Text must not be empty');<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setHourToNoon(ev);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.attachEvent('onEventChanged', (id, ev) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setHourToNoon(ev);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }, {});<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.init(this.schedulerContainer.nativeElement, new Date(), 'timeline');<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; ngOnDestroy() {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const scheduler = this.scheduler;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler &amp;&amp; scheduler.destructor();<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; getRoom(key: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return this.rooms.find(room =&gt; room.key === key) || null;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getRoomType(key: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const roomType = this.roomTypes.find(item =&gt; item.key === key);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return roomType ? roomType.label : null;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getCleaningStatus(key: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const cleaningStatus = this.cleaningStatuses.find(item =&gt; item.key === key);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return cleaningStatus ? cleaningStatus.label : null;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getCleaningStatusIndicator(key: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const cleaningStatus = this.cleaningStatuses.find(item =&gt; item.key === key);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return cleaningStatus ? cleaningStatus.color : null;<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; getBookingStatus(key: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const bookingStatus = this.bookingStatuses.find(item =&gt; item.key === key);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return bookingStatus ? bookingStatus.label : '';<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; handleCleaningStatusChange(target: HTMLSelectElement) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const roomId = target.getAttribute('room-id');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const selectedCleaningStatus = target.value;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const roomToUpdate = this.rooms.find(room =&gt; room.id == roomId);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; if (roomToUpdate) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roomToUpdate.cleaning_status = selectedCleaningStatus;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const backgroundColor = this.getCleaningStatusIndicator(selectedCleaningStatus);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; target.style.backgroundColor = backgroundColor;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; generateCleaningStatusColumnTemplate(room: Room) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const backgroundColor = this.getCleaningStatusIndicator(room.cleaning_status);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const rgbaBackgroundColor = this.hexToRgba(backgroundColor, 0.2);<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const selectHTML = [`<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;select class='cleaning-status-select'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; room-id='${room.id}'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style='width: 100%; height: 52px; background-color: ${rgbaBackgroundColor}; outline: none; border: none;'&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; `];<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.cleaningStatuses.forEach(status =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const optionHTML = `<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;option value='${status.key}' style='background-color: ${status.color};' ${room.cleaning_status === status.key ? 'selected' : ''}&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ${this.getCleaningStatus(status.key)}<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;\/option&gt;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selectHTML.push(optionHTML);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; selectHTML.push(`&lt;\/select&gt;`);<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return selectHTML.join('');<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; hexToRgba(hex: any, alpha: any) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const r = parseInt(hex.slice(1, 3), 16);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const g = parseInt(hex.slice(3, 5), 16);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const b = parseInt(hex.slice(5, 7), 16);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return `rgba(${r}, ${g}, ${b}, ${alpha})`;<br \/>\n&nbsp; &nbsp; }<br \/>\n}<\/div><\/div>\n<p>Feel free to check out the <a href=\"https:\/\/github.com\/DHTMLX\/angular-scheduler-hotel-booking\/blob\/main\/src\/app\/scheduler\/scheduler.component.ts\" rel=\"noopener\" target=\"_blank\">complete code of <em>scheduler.component.ts<\/em><\/a> on GitHub.<\/p>\n<p>Now let&#8217;s add the new component to the page. For this purpose, open <em>app.component.html<\/em> (located in <em>src\/app<\/em>) and insert the scheduler tag in there:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">&lt;scheduler&gt;&lt;\/scheduler&gt;<\/div><\/div>\n<p>At the next step, we will proceed to load and save data.<\/p>\n<h5>Step 4 \u2013 Providing and Saving Data<\/h5>\n<h6>Data Loading<\/h6>\n<p>To add data loading to the Angular Scheduler, you need to add the <strong>reservation<\/strong> and <strong>collections<\/strong> services. But before that, let&#8217;s create and configure an environment file for a project. Run the following command:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">ng generate environments<\/div><\/div>\n<p>In the newly created <em>environment.development.ts<\/em> file inside the <em>src\/environments<\/em> folder, we will add the following code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">export const environment = {<br \/>\n&nbsp; &nbsp; production: false,<br \/>\n&nbsp; &nbsp; apiBaseUrl: 'http:\/\/localhost:3000\/data'<br \/>\n};<\/div><\/div>\n<p>Let&#8217;s also create a helper for errors that will notify users by sending error messages to the console when something goes wrong. To do so, create the <em>service-helper.ts<\/em> file inside the <em>app\/services<\/em> folder with the following code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">export function HandleError(error: any): Promise&lt;any&gt;{<br \/>\n&nbsp; &nbsp; console.log(error);<br \/>\n&nbsp; &nbsp; return Promise.reject(error);<br \/>\n}<\/div><\/div>\n<p>Now let&#8217;s create the <strong>reservations<\/strong> and <strong>collections<\/strong> services. Run the following commands:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">ng generate service services\/reservation --flat --skip-tests<br \/>\nng generate service services\/collections --flat --skip-tests<\/div><\/div>\n<p>In the newly created <em>reservation.service.ts<\/em> file inside the <em>services<\/em> folder, we will add the following code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { Injectable } from '@angular\/core';<br \/>\nimport { Reservation } from &quot;..\/models\/reservation&quot;;<br \/>\nimport { HttpClient } from &quot;@angular\/common\/http&quot;;<br \/>\nimport { HandleError } from &quot;.\/service-helper&quot;;<br \/>\nimport { firstValueFrom } from 'rxjs';<br \/>\nimport { environment } from '..\/..\/environments\/environment.development';<br \/>\n<br \/>\n<br \/>\n@Injectable()<br \/>\nexport class ReservationService {<br \/>\n&nbsp; &nbsp; private reservationUrl = `${environment.apiBaseUrl}\/reservations`;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; constructor(private http: HttpClient) { }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; get(): Promise&lt;Reservation[]&gt;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.get(this.reservationUrl))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; insert(reservation: Reservation): Promise&lt;Reservation&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.post(this.reservationUrl, reservation))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; update(reservation: Reservation): Promise&lt;void&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.put(`${this.reservationUrl}\/${reservation.id}`, reservation))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; remove(id: number): Promise&lt;void&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.delete(`${this.reservationUrl}\/${id}`))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n}<\/div><\/div>\n<p>In the newly created <em>collections.service.ts<\/em> file, add the next lines of code:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">import { Injectable } from '@angular\/core';<br \/>\nimport { Room } from &quot;..\/models\/room.model&quot;;<br \/>\nimport { RoomType } from &quot;..\/models\/room-type.model&quot;;<br \/>\nimport { CleaningStatus } from &quot;..\/models\/cleaning-status.model&quot;;<br \/>\nimport { BookingStatus } from &quot;..\/models\/booking-status.model&quot;;<br \/>\nimport { HttpClient } from &quot;@angular\/common\/http&quot;;<br \/>\nimport { HandleError } from &quot;.\/service-helper&quot;;<br \/>\nimport { firstValueFrom } from 'rxjs';<br \/>\nimport { environment } from '..\/..\/environments\/environment.development';<br \/>\n<br \/>\n<br \/>\n@Injectable()<br \/>\nexport class CollectionsService {<br \/>\n&nbsp; &nbsp; private collectionsUrl = `${environment.apiBaseUrl}\/collections`;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; constructor(private http: HttpClient) { }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getRooms(): Promise&lt;Room[]&gt;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.get(`${this.collectionsUrl}\/rooms`))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; updateRoom(room: Room): Promise&lt;void&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.put(`${this.collectionsUrl}\/rooms\/${room.id}`, room))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getRoomTypes(): Promise&lt;RoomType[]&gt;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.get(`${this.collectionsUrl}\/roomTypes`))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getCleaningStatuses(): Promise&lt;CleaningStatus[]&gt;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.get(`${this.collectionsUrl}\/cleaningStatuses`))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; getBookingStatuses(): Promise&lt;BookingStatus[]&gt;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return firstValueFrom(this.http.get(`${this.collectionsUrl}\/bookingStatuses`))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(HandleError);<br \/>\n&nbsp; &nbsp; }<br \/>\n}<\/div><\/div>\n<p>The <strong>get()<\/strong>, <strong>getRooms()<\/strong>, <strong>getRoomTypes()<\/strong>, <strong>getCleaningStatuses()<\/strong> and <strong>getBookingStatuses()<\/strong> methods retrieve data from the server.<\/p>\n<p>The <strong>reservationUrl<\/strong> and <strong>collectionsUrl<\/strong> are private elements of the services. They contain the URL to the REST API. In order to send HTTP requests, an HTTP class has been injected into the service.<\/p>\n<p>To insert a new item, you need to send a POST request to the URL with the new item in its body.<\/p>\n<p>To update an item, you need to send a PUT request to the <em>url\/item_id<\/em>. This request also contains the updated item in its body.<br \/>\nTo remove an item, you need to send a delete request to the <em>url\/item_id<\/em>.<\/p>\n<h6>CRUD Operations<\/h6>\n<p>The services should handle CRUD operations in the scheduler. HTTP communication has been enabled by adding the <strong>HttpClient<\/strong> module in the <em>reservations.service.ts<\/em> and <em>collections.service.ts<\/em> files:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { HttpClient } from &quot;@angular\/common\/http&quot;;<\/div><\/div>\n<p>This step allows fetching data seamlessly within our Angular application.<br \/>\nTo utilize the <strong>HttpClient<\/strong> module, it is also required to include the essential <strong>HttpClientModule<\/strong> from the <em>@angular\/common\/http<\/em> package. In the <em>app.module.ts<\/em> file, you should update the imports array as follows:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { NgModule } from '@angular\/core';<br \/>\nimport { BrowserModule } from '@angular\/platform-browser';<br \/>\n<br \/>\n<br \/>\nimport { AppRoutingModule } from '.\/app-routing.module';<br \/>\nimport { AppComponent } from '.\/app.component';<br \/>\nimport { SchedulerComponent } from '.\/scheduler\/scheduler.component';<br \/>\n<br \/>\n<br \/>\nimport { FormsModule } from '@angular\/forms';<br \/>\nimport { HttpClientModule } from '@angular\/common\/http';<br \/>\n<br \/>\n<br \/>\n@NgModule({<br \/>\n&nbsp; &nbsp; declarations: [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; AppComponent,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; SchedulerComponent<br \/>\n&nbsp; &nbsp; ],<br \/>\n&nbsp; &nbsp; imports: [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; BrowserModule,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; AppRoutingModule,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; FormsModule,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; HttpClientModule<br \/>\n&nbsp; &nbsp; ],<br \/>\n&nbsp; &nbsp; providers: [],<br \/>\n&nbsp; &nbsp; bootstrap: [AppComponent]<br \/>\n})<br \/>\nexport class AppModule { }<\/div><\/div>\n<p>The scheduler component is supposed to use the <strong>ReservationService<\/strong> and <strong>CollectionsService<\/strong> to get\/insert\/update\/remove reservations and collections. To enable these options, let&#8217;s add the <strong>ReservationService<\/strong> and <strong>CollectionsService<\/strong> to the component. First, import the necessary modules for the service in <em>scheduler.component.ts<\/em>:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { ReservationService } from '..\/services\/reservation.service';<br \/>\nimport { CollectionsService } from '..\/services\/collections.service';<\/div><\/div>\n<p>You should also specify the <strong>EventService<\/strong> as a provider in the <strong>@Component<\/strong> decorator:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">providers: [ ReservationService, CollectionsService ]<\/div><\/div>\n<p>Now every time a new <strong>SchedulerComponent<\/strong> is initialized, a fresh instance of the service will be created.<br \/>\nThe services should be prepared to be injected into the component. For this purpose, add the following constructor to the <strong>SchedulerComponent<\/strong> class:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">constructor(<br \/>\n&nbsp; &nbsp; private reservationService: ReservationService,<br \/>\n&nbsp; &nbsp; private collectionsService: CollectionsService<br \/>\n) { }<\/div><\/div>\n<p>Next, we&#8217;ll add the <strong>updateRoom()<\/strong> method to save the room cleaning status changes in the database:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">handleCleaningStatusChange(target: HTMLSelectElement) {<br \/>\n&nbsp; &nbsp; ...<br \/>\n&nbsp; &nbsp; this.collectionsService.updateRoom(roomToUpdate);<br \/>\n}<\/div><\/div>\n<p>You need to modify the <strong>ngOnInit<\/strong> function to call the services to get the function and then wait for a response to put the data to the scheduler.<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">scheduler.init(this.schedulerContainer.nativeElement, new Date(), 'timeline');<br \/>\n<br \/>\n<br \/>\nconst dp = scheduler.createDataProcessor({<br \/>\n&nbsp; &nbsp; event: {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; create: (data: Reservation) =&gt; this.reservationService.insert(data),<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; update: (data: Reservation) =&gt; this.reservationService.update(data),<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; delete: (id: number) =&gt; this.reservationService.remove(id),<br \/>\n&nbsp; &nbsp; }<br \/>\n});<br \/>\n<br \/>\n<br \/>\nforkJoin({<br \/>\n&nbsp; &nbsp; reservations: this.reservationService.get(),<br \/>\n&nbsp; &nbsp; rooms: this.collectionsService.getRooms(),<br \/>\n&nbsp; &nbsp; roomTypes: this.collectionsService.getRoomTypes(),<br \/>\n&nbsp; &nbsp; cleaningStatuses: this.collectionsService.getCleaningStatuses(),<br \/>\n&nbsp; &nbsp; bookingStatuses: this.collectionsService.getBookingStatuses()<br \/>\n}).subscribe({<br \/>\n&nbsp; &nbsp; next: ({ reservations, rooms, roomTypes, cleaningStatuses, bookingStatuses }) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; const data = {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; events: reservations,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; collections: {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rooms,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roomTypes,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cleaningStatuses,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bookingStatuses,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; };<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; scheduler.parse(data);<br \/>\n&nbsp; &nbsp; },<br \/>\n&nbsp; &nbsp; error: error =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; console.error('An error occurred:', error);<br \/>\n&nbsp; &nbsp; }<br \/>\n});<\/div><\/div>\n<p>The <strong>scheduler.parse<\/strong> accepts a <a href=\"https:\/\/docs.dhtmlx.com\/scheduler\/data_formats.html#json\" rel=\"noopener\" target=\"_blank\">data object in the JSON format<\/a>. To efficiently wait for the completion of multiple asynchronous requests and load their data (reservations and collections) into the scheduler, you can utilize the <strong>forkJoin<\/strong> operator from the RxJS library. Please include the import:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">import { forkJoin } from 'rxjs';<\/div><\/div>\n<p>You can check the complete code for the <a href=\"https:\/\/github.com\/DHTMLX\/angular-scheduler-hotel-booking\/blob\/main\/src\/app\/scheduler\/scheduler.component.ts\" rel=\"noopener\" target=\"_blank\"><em>scheduler.components.ts<\/em> file on GitHub<\/a>.<\/p>\n<h5>Step 5 \u2013 Server Configuration<\/h5>\n<p>Now, let&#8217;s move on to setting up the Node.js server for our app.<br \/>\nThis tutorial uses the <a href=\"https:\/\/expressjs.com\/\" rel=\"noopener\" target=\"_blank\">Express<\/a> framework and MySQL as a data storage.<\/p>\n<h6>Adding dependencies and installing modules<\/h6>\n<p>You should set up your MySQL server, or you can use another service, e.g. <a href=\"https:\/\/www.freemysqlhosting.net\/\" rel=\"noopener\" target=\"_blank\">Free MySQL Hosting<\/a>.<br \/>\nAdd express, mysql, and date-format-lite modules:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ npm install express mysql date-format-lite<\/div><\/div>\n<p>The <em>server.js<\/em> has been specified as the entry point above. Now let&#8217;s create the <em>server<\/em> folder in the root of the project and add the <em>server.js<\/em> file with the code below:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">const express = require('express'); \/\/ use Express<br \/>\nconst app = express(); \/\/ create application<br \/>\nconst port = 3000; \/\/ port for listening<br \/>\nconst cors = require('cors');<br \/>\napp.use(cors()); \/\/ enable CORS for all routes<br \/>\n<br \/>\n<br \/>\n\/\/ MySQL will be used for db access and util to promisify queries<br \/>\nconst util = require('util');<br \/>\nconst mysql = require('mysql');<br \/>\n<br \/>\n<br \/>\n\/\/ use your own parameters for database<br \/>\nconst mysqlConfig = {<br \/>\n&nbsp; &nbsp; 'connectionLimit': 10,<br \/>\n&nbsp; &nbsp; 'host': 'localhost',<br \/>\n&nbsp; &nbsp; 'user': 'root',<br \/>\n&nbsp; &nbsp; 'password': '',<br \/>\n&nbsp; &nbsp; 'database': 'room_reservation_node'<br \/>\n};<br \/>\n<br \/>\n<br \/>\napp.use(express.json()); \/\/ Enable JSON body parsing<br \/>\n\/\/ return static pages from the '.\/public' directory<br \/>\napp.use(express.static(__dirname + '\/public'));<br \/>\n<br \/>\n<br \/>\n\/\/ start server<br \/>\napp.listen(port, () =&gt; {<br \/>\n&nbsp; &nbsp; console.log('Server is running on port ' + port + '...');<br \/>\n});<br \/>\n<br \/>\n<br \/>\nconst router = require('.\/router');<br \/>\n<br \/>\n<br \/>\n\/\/ open connection to mysql<br \/>\nconst connectionPool = mysql.createPool(mysqlConfig);<br \/>\nconnectionPool.query = util.promisify(connectionPool.query);<br \/>\n<br \/>\n<br \/>\n\/\/ add listeners to basic CRUD requests<br \/>\nconst DatabaseHandler = require('.\/databaseHandler');<br \/>\nconst databaseHandler = new DatabaseHandler(connectionPool);<br \/>\nrouter.setRoutes(app, '\/data', databaseHandler);<\/div><\/div>\n<p>Then open the <em>package.json<\/em> file and replace the <strong>start<\/strong> statement with:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">&quot;scripts&quot;: {<br \/>\n&nbsp; &nbsp; &quot;ng&quot;: &quot;ng&quot;,<br \/>\n&nbsp; &nbsp; &quot;start&quot;: &quot;concurrently &quot;node server\/server.js&quot; &quot;ng serve&quot;&quot;,<br \/>\n&nbsp; &nbsp; \u2026<\/div><\/div>\n<p>We&#8217;ll utilize the <em>concurrently<\/em> package to enable simultaneous launching of both the server and the client application.<br \/>\nSo, add the <strong>concurrently<\/strong> module:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ npm install concurrently<\/div><\/div>\n<h6>Preparing a database<\/h6>\n<p>Let&#8217;s connect Scheduler to the database and define methods to read and write items in it.<\/p>\n<ul>\n<li><strong><h7>Creating a database:<\/h7><\/strong><\/li>\n<\/ul>\n<p>First things first, we need a database to work with. You can create a database with your favorite mysql-client or via the console.<br \/>\nTo create a database with a mysql-client, open it and execute the code below. For creating <strong>reservations<\/strong> tables:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">CREATE TABLE `reservations` (<br \/>\n`id` bigint(20) unsigned AUTO_INCREMENT,<br \/>\n`start_date` datetime NOT NULL,<br \/>\n`end_date` datetime NOT NULL,<br \/>\n`text` varchar(255) DEFAULT NULL,<br \/>\n`room` varchar(255) DEFAULT NULL,<br \/>\n`booking_status` varchar(255) DEFAULT NULL,<br \/>\n`is_paid` BOOLEAN DEFAULT NULL CHECK (is_paid IN (0, 1)),<br \/>\nPRIMARY KEY (`id`)<br \/>\n) DEFAULT CHARSET=utf8;<\/div><\/div>\n<p>Let&#8217;s add some test data:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">INSERT INTO `reservations` VALUES (2, '2023-08-01', '2023-08-11', 'RSV2023-08-01ABC124', 3, 4, true);<br \/>\nINSERT INTO `reservations` VALUES (3, '2023-08-07', '2023-08-17', 'RSV2023-08-07ABC126', 5, 3, true);<br \/>\nINSERT INTO `reservations` VALUES (4, '2023-08-04', '2023-08-16', 'RSV2023-08-04ABC125', 7, 4, false);<br \/>\nINSERT INTO `reservations` VALUES (13, '2023-07-28', '2023-08-14', 'RSV2023-07-28ABC123', 1, 4, true);<br \/>\nINSERT INTO `reservations` VALUES (14, '2023-08-14', '2023-08-27', 'RSV2023-08-14ABC129', 1, 3, false);<br \/>\nINSERT INTO `reservations` VALUES (15, '2023-08-19', '2023-08-29', 'new booking', 4, 1, false);<br \/>\nINSERT INTO `reservations` VALUES (16, '2023-08-24', '2023-08-31', 'new booking', 11, 1, false);<br \/>\nINSERT INTO `reservations` VALUES (17, '2023-08-17', '2023-08-26', 'RSV2023-08-17ABC135', 6, 2, false);<br \/>\nINSERT INTO `reservations` VALUES (18, '2023-08-18', '2023-08-31', 'RSV2023-08-18ABC139', 9, 2, false);<br \/>\nINSERT INTO `reservations` VALUES (19, '2023-08-02', '2023-08-12', 'RSV2023-08-02ABC127', 10, 4, true);<br \/>\nINSERT INTO `reservations` VALUES (20, '2023-08-12', '2023-08-21', 'RSV2023-08-12ABC130', 10, 3, false);<\/div><\/div>\n<p>For creating <strong>rooms<\/strong> tables:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">CREATE TABLE `rooms` (<br \/>\n`id` bigint(20) unsigned AUTO_INCREMENT,<br \/>\n`value` varchar(255) DEFAULT NULL,<br \/>\n`label` varchar(255) DEFAULT NULL,<br \/>\n`type` varchar(255) DEFAULT NULL,<br \/>\n`cleaning_status` varchar(255) DEFAULT NULL,<br \/>\nPRIMARY KEY (`id`)<br \/>\n) DEFAULT CHARSET=utf8;<\/div><\/div>\n<p>Let&#8217;s add some test data:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">INSERT INTO `rooms` VALUES ('1', '1', '101', '1', '1');<br \/>\nINSERT INTO `rooms` VALUES ('2', '2', '102', '1', '3');<br \/>\nINSERT INTO `rooms` VALUES ('3', '3', '103', '1', '2');<br \/>\nINSERT INTO `rooms` VALUES ('4', '4', '104', '1', '1');<br \/>\nINSERT INTO `rooms` VALUES ('5', '5', '105', '2', '1');<br \/>\nINSERT INTO `rooms` VALUES ('6', '6', '201', '2', '2');<br \/>\nINSERT INTO `rooms` VALUES ('7', '7', '202', '2', '1');<br \/>\nINSERT INTO `rooms` VALUES ('8', '8', '203', '3', '3');<br \/>\nINSERT INTO `rooms` VALUES ('9', '9', '204', '3', '3');<br \/>\nINSERT INTO `rooms` VALUES ('10', '10', '301', '4', '2');<br \/>\nINSERT INTO `rooms` VALUES ('11', '11', '302', '4', '2');<br \/>\nINSERT INTO `rooms` VALUES ('12', '12', '303', '1', '2');<\/div><\/div>\n<p>For creating the <strong>roomTypes<\/strong> tables:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">CREATE TABLE `roomTypes` (<br \/>\n`id` bigint(20) unsigned AUTO_INCREMENT,<br \/>\n`value` varchar(255) DEFAULT NULL,<br \/>\n`label` varchar(255) DEFAULT NULL,<br \/>\nPRIMARY KEY (`id`)<br \/>\n) DEFAULT CHARSET=utf8;<\/div><\/div>\n<p>Let&#8217;s add some test data:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">INSERT INTO `roomTypes` VALUES ('1', '1', '1 bed');<br \/>\nINSERT INTO `roomTypes` VALUES ('2', '2', '2 bed');<br \/>\nINSERT INTO `roomTypes` VALUES ('3', '3', '3 bed');<br \/>\nINSERT INTO `roomTypes` VALUES ('4', '4', '4 bed');<\/div><\/div>\n<p>For creating <strong>cleaningStatuses<\/strong> tables:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">CREATE TABLE `cleaningStatuses` (<br \/>\n`id` bigint(20) unsigned AUTO_INCREMENT,<br \/>\n`value` varchar(255) DEFAULT NULL,<br \/>\n`label` varchar(255) DEFAULT NULL,<br \/>\n`color` varchar(255) DEFAULT NULL,<br \/>\nPRIMARY KEY (`id`)<br \/>\n) DEFAULT CHARSET=utf8;<\/div><\/div>\n<p>Let&#8217;s add some test data:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">INSERT INTO `cleaningStatuses` VALUES ('1', '1', 'Ready', '#43a047');<br \/>\nINSERT INTO `cleaningStatuses` VALUES ('2', '2', 'Dirty', '#e53935');<br \/>\nINSERT INTO `cleaningStatuses` VALUES ('3', '3', 'Clean up', '#ffb300');<\/div><\/div>\n<p>For creating <strong>bookingStatuses<\/strong> tables:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">CREATE TABLE `bookingStatuses` (<br \/>\n`id` bigint(20) unsigned AUTO_INCREMENT,<br \/>\n`value` varchar(255) DEFAULT NULL,<br \/>\n`label` varchar(255) DEFAULT NULL,<br \/>\nPRIMARY KEY (`id`)<br \/>\n) DEFAULT CHARSET=utf8;<\/div><\/div>\n<p>Let&#8217;s add some test data:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">INSERT INTO `bookingStatuses` VALUES ('1', '1', 'New');<br \/>\nINSERT INTO `bookingStatuses` VALUES ('2', '2', 'Confirmed');<br \/>\nINSERT INTO `bookingStatuses` VALUES ('3', '3', 'Arrived');<br \/>\nINSERT INTO `bookingStatuses` VALUES ('4', '4', 'Checked Out');<\/div><\/div>\n<ul>\n<li><strong><h7>Implementing data access:<\/h7><\/strong><\/li>\n<\/ul>\n<p>All the read\/write logic will be defined in a separate module called <strong>DatabaseHandler<\/strong>. It&#8217;ll take a mysql connection and perform simple CRUD operations in the specified table: read all the items, insert new items, update or delete the existing ones. For this, create the <em>databaseHandler.js<\/em> file and add the code below into it:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">require('date-format-lite'); \/\/ add date format<br \/>\n<br \/>\n<br \/>\nclass DatabaseHandler {<br \/>\n&nbsp; &nbsp; constructor(connection, table) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this._db = connection;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; this.table = 'reservations';<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 reservations handler \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; \/\/ get reservations, use dynamic loading if parameters sent<br \/>\n&nbsp; &nbsp; async getAllReservations(params) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let query = 'SELECT * FROM ??';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let queryParams = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.table<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(query, queryParams);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; result.forEach((entry) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ format date and time<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entry.start_date = entry.start_date.format('YYYY-MM-DD hh:mm');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entry.end_date = entry.end_date.format('YYYY-MM-DD hh:mm');<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; });<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return result;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/ create new reservation<br \/>\n&nbsp; &nbsp; async insert(data) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'INSERT INTO ?? (`start_date`, `end_date`, `text`, `room`, `booking_status`, `is_paid`) VALUES (?,?,?,?,?,?)',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [this.table, data.start_date, data.end_date, data.text, data.room, data.booking_status, data.is_paid]);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; action: 'inserted',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tid: result.insertId<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/ update reservation<br \/>\n&nbsp; &nbsp; async update(id, data) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; await this._db.query(<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'UPDATE ?? SET `start_date` = ?, `end_date` = ?, `text` = ?, `room` = ?, `booking_status` = ?, `is_paid` = ? WHERE id = ?',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [this.table, data.start_date, data.end_date, data.text, data.room, data.booking_status, data.is_paid, id]);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; action: 'updated'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/ delete reservation<br \/>\n&nbsp; &nbsp; async delete(id) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; await this._db.query(<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'DELETE FROM ?? WHERE `id`=? ;',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [this.table, id]);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; action: 'deleted'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 reservations handler \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 room cleanup status handler \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; \/\/ get rooms<br \/>\n&nbsp; &nbsp; async getAllRooms(params) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let query = 'SELECT * FROM ??';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let queryParams = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'rooms'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(query, queryParams);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return result;<br \/>\n&nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/ update room cleanup status<br \/>\n&nbsp; &nbsp; async updateRoomCleaningStatus(id, data) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; await this._db.query(<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'UPDATE ?? SET `value` = ?, `label` = ?, `type` = ?, `cleaning_status` = ? WHERE id = ?',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ['rooms', data.key, data.label, data.type, data.cleaning_status, id]);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; action: 'updated'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 room cleanup status handler \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 get room types \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; async getRoomTypes(params) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let query = 'SELECT * FROM ??';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let queryParams = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'roomTypes'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(query, queryParams);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return result;<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 get room types \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 get cleaning statuses \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; async getCleaningStatuses(params) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let query = 'SELECT * FROM ??';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let queryParams = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'cleaningStatuses'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(query, queryParams);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return result;<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 get cleaning statuses \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 get booking statuses \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; async getBookingStatuses(params) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let query = 'SELECT * FROM ??';<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let queryParams = [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'bookingStatuses'<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ];<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result = await this._db.query(query, queryParams);<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; return result;<br \/>\n&nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 get booking statuses \u2191\u2191\u2191<br \/>\n}<br \/>\n<br \/>\n<br \/>\nmodule.exports = DatabaseHandler;<\/div><\/div>\n<h6>Routing<\/h6>\n<p>Then you need to set up routes, so that the storage could be accessed by the scheduler you have placed on the page.<br \/>\nFor this, create another helper module and call it <em>router.js<\/em>:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;height:600px;\"><div class=\"text codecolorer\">function callMethod (method) {<br \/>\n&nbsp; &nbsp; return async (req, res) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; let result;<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; try {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result = await method(req, res);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; } catch (e) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result = &nbsp;{<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; action: 'error',<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; message: e.message<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; res.send(result);<br \/>\n&nbsp; &nbsp; }<br \/>\n};<br \/>\n<br \/>\n<br \/>\nmodule.exports = {<br \/>\n&nbsp; &nbsp; setRoutes (app, prefix, databaseHandler) {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 reservations router \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.get(`${prefix}\/reservations`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.getAllReservations(req.query);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.post(`${prefix}\/reservations`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.insert(req.body);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.put(`${prefix}\/reservations\/:id`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.update(req.params.id, req.body);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.delete(`${prefix}\/reservations\/:id`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.delete(req.params.id);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 reservations router \u2191\u2191\u2191<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 rooms router \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.get(`${prefix}\/collections\/rooms`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.getAllRooms(req.query);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.put(`${prefix}\/collections\/rooms\/:id`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.updateRoomCleaningStatus(req.params.id, req.body);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 rooms router \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 room types router \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.get(`${prefix}\/collections\/roomTypes`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.getRoomTypes(req.query);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 room types router \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 cleaning statuses router \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.get(`${prefix}\/collections\/cleaningStatuses`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.getCleaningStatuses(req.query);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 cleaning statuses router \u2191\u2191\u2191<br \/>\n<br \/>\n<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2193\u2193\u2193 booking statuses router \u2193\u2193\u2193<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; app.get(`${prefix}\/collections\/bookingStatuses`, callMethod((req) =&gt; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return databaseHandler.getBookingStatuses(req.query);<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }));<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/\/ \u2191\u2191\u2191 booking statuses router \u2191\u2191\u2191<br \/>\n&nbsp; &nbsp; }<br \/>\n};<\/div><\/div>\n<p>All it does is sets up the application to listen to request URLs that scheduler can send and calls the appropriate methods of the storage. Note that all methods are wrapped into try-catch blocks for you to be able to capture any error and return an appropriate error response to the client. Learn more about <a href=\"https:\/\/docs.dhtmlx.com\/scheduler\/server_integration.html#errorhandling\" rel=\"noopener\" target=\"_blank\">error handling<\/a>.<\/p>\n<p>Also note that the exception message is written directly to the API response. It&#8217;s pretty handy during the development, but in the production environment it can be a good idea to hide these messages from the client side, since raw mysql exceptions that get there may contain sensitive data.<\/p>\n<p>Now if you open the app page, you should see a scheduler with reservations. You can create, delete, and modify items within the scheduler. Any changes you make will be retained even if you reload the page.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1.jpg\" alt=\"Angular Hotel Booking App\" width=\"957\" height=\"744\" class=\"aligncenter size-full wp-image-11748\" srcset=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1.jpg 957w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1-300x233.jpg 300w, https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2023\/09\/Scheduler-with-Angular-1-768x597.jpg 768w\" sizes=\"auto, (max-width: 957px) 100vw, 957px\" \/><\/p>\n<h5>Conclusions<\/h5>\n<p>The Hotel Booking Calendar with Angular is ready! You are welcome to check out the <a href=\"https:\/\/github.com\/DHTMLX\/angular-scheduler-hotel-booking\" rel=\"noopener\" target=\"_blank\">full source code on GitHub<\/a>. The app can use RESTful API for CRUD operations.<\/p>\n<p>We hope this guide has given you a clear understanding of how to integrate the room booking app based on DHTMLX Scheduler with Angular and connect it to a real backend. We&#8217;ll be glad if it helps you achieve your application development goals.<\/p>\n<p>If you have any thoughts and ideas about the next topics\/tutorials you would like to discover, don&#8217;t hesitate to share them in the comments.<\/p>\n<h5>Related Materials:<\/h5>\n<ul>\n<li><a href=\"https:\/\/dhtmlx.com\/docs\/products\/dhtmlxScheduler\/download.shtml\" rel=\"noopener\" target=\"_blank\">DHTMLX Scheduler 30-day free trial<\/a><\/li>\n<li><a href=\"https:\/\/dhtmlx.com\/docs\/products\/demoApps\/room-reservation-html5-js-php\/\" rel=\"noopener\" target=\"_blank\">Hotel Room Reservation Demo in PHP\/MySQL and JavaScript<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/DHTMLX\/angular-scheduler-hotel-booking\/blob\/main\/src\/app\/scheduler\/scheduler.component.ts\" rel=\"noopener\" target=\"_blank\">Hotel Booking Demo with DHTMLX Scheduler and Angular on GitHub<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Welcome to this tutorial, where we bring together two powerful tools: the DHTMLX Scheduler library and the Angular framework, to create a comprehensive hotel room booking application. In this &#8230;<\/p>\n","protected":false},"author":101012,"featured_media":11770,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[71,1],"tags":[33,59,15],"class_list":["post-11747","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","category-uncategorized","tag-all-posts","tag-angular","tag-dhtmlxscheduler"],"_links":{"self":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/11747","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/users\/101012"}],"replies":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/comments?post=11747"}],"version-history":[{"count":19,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/11747\/revisions"}],"predecessor-version":[{"id":14380,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/11747\/revisions\/14380"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/media\/11770"}],"wp:attachment":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/media?parent=11747"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/categories?post=11747"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/tags?post=11747"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}