<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Nishka Kotian - SashiDo.io | API Development, Deployment and Scaling made simple.]]></title><description><![CDATA[SashiDo.io is a serverless API development platform with scalable json rest and graphql apis, headless cms, built with nodejs, mongodb, parse server, kubernetes and docker.]]></description><link>https://blog.sashido.io/</link><image><url>https://blog.sashido.io/favicon.png</url><title>Nishka Kotian - SashiDo.io | API Development, Deployment and Scaling made simple.</title><link>https://blog.sashido.io/</link></image><generator>Ghost 1.20</generator><lastBuildDate>Tue, 26 May 2026 05:00:44 GMT</lastBuildDate><atom:link href="https://blog.sashido.io/author/nishka/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How to Build a Venue Booking App with SashiDo - Part 2]]></title><description><![CDATA[Part 2 of a step-by-step guide on how to make your own venue booking web app using a NodeJS backend from SashiDo. The tutorial is perfect for beginners, who are looking for a fun project to start their JavaScript coding journey.]]></description><link>https://blog.sashido.io/building-a-venue-booking-system-part-2/</link><guid isPermaLink="false">612b3cd2dcc2ad002048c508</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Project]]></category><dc:creator><![CDATA[Nishka Kotian]]></dc:creator><pubDate>Tue, 31 Aug 2021 12:00:12 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/08/cover-pin-3.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2021/08/cover-pin-3.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"><p>Hello again! This post is a continuation to the <a href="https://blog.sashido.io/venue-booking-system-part1">first part</a> where I've explained about user authentication and storing venue details for my Venue Booking App. Today, we will cover querying the database and also look into how the booking functionality can be implemented.<br>
In case you aren't familiar with ES6 arrow functions and template literals I would suggest reading about them first. I have added useful links at the bottom of the tutorial for your reference.</p>
<h2 id="tableofcontents">Table Of Contents</h2>
<ul>
<li><a href="#chapter-1">Displaying venues on the dashboard</a></li>
<li><a href="#chapter-2">Filtering venues based on location</a></li>
<li><a href="#chapter-3">Show venue information</a></li>
<li><a href="#chapter-4">Sending booking request to owner</a></li>
<li><a href="#chapter-5">Approving requests</a></li>
<li><a href="#chapter-6">Deleting past bookings</a></li>
<li><a href="#chapter-7">Conclusion</a></li>
<li><a href="#chapter-8">Useful links</a></li>
</ul>
<h3 id="displayingvenuesonthedashboardanamechapter1a">Displaying venues on the dashboard <a name="chapter-1"></a></h3>
<p>After the owner has added venues, there has to be a way to get all that information. In the customer's dashboard top 25 venues are shown and in the owner's dashboard under the Venues tab all the venues belonging to that owner are displayed.</p>
<p>Owner's dashboard:<br>
<img src="https://media-blog.sashido.io/content/images/2021/08/sked_OwnerDashboard.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>This owner has 3 venues listed.</p>
<p>When an owner first creates an account, they wouldn't have any venues. So in the html I have created two divs one for new owners with no venues and another for those who have at least one venue.</p>
<pre><code class="language-html">&lt;body onload=&quot;getOwnerData();&quot;&gt;

...

&lt;!--Div to display for new owners.No venues added yet--&gt;
&lt;div class=&quot;card mt-4 w-75 defaultMsg d-none&quot; id=&quot;novenues&quot;&gt;
    &lt;div class=&quot;card-body&quot;&gt;
        &lt;h5 class=&quot;mb-3&quot; style=&quot;color: #fa5757;&quot;&gt;Welcome to SKED!&lt;/h5&gt;
        Your listed venues will show up here after you add them.&lt;br&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn mt-3&quot; style=&quot;background:#7cebeb;&quot;
            data-bs-toggle=&quot;modal&quot; data-bs-target=&quot;#addVenueModal&quot;&gt;Add a
            venue&lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;!--Div to display for owners with at least 1 venue added--&gt;
&lt;div id=&quot;displayVenues&quot; class=&quot;row&quot;&gt;
    &lt;h5 class=&quot;mt-4 mb-3 d-none&quot; id=&quot;yourVenuesHeading&quot;&gt;Your Venues&lt;/h5&gt;
&lt;/div&gt;   

...

&lt;/body&gt;               
</code></pre>
<pre><code class="language-javascript">function getOwnerData() {
    const user = Parse.User.current();
    document.getElementById(&quot;ownername&quot;).innerHTML = user.attributes.username;

    const Venues = Parse.Object.extend(&quot;Venues&quot;);
    const query = new Parse.Query(Venues);
    query.equalTo(&quot;owner&quot;, user);
    query.find().then(function findVenues(results) {
        if (results.length == 0) {
            document.getElementById(&quot;novenues&quot;).classList.remove(&quot;d-none&quot;);
        } else {
            document.getElementById(&quot;yourVenuesHeading&quot;).classList.remove(&quot;d-none&quot;);
            const displayArea = document.getElementById(&quot;displayVenues&quot;);
            results.forEach((venue, index) =&gt; {
                if (i == 11) { i = 0; }
                displayVenue(displayArea, venue);
                i += 1;
            });
        }

        /* Insert code to fetch booking details here */

    }, function error(err) {
        console.log('Error : ', err);
    });
}
</code></pre>
<p>To fetch the data, you have to find out the owner who is logged in using <code>Parse.User.current()</code> and then create a new <code>Parse.Query</code> which can be used for querying objects from the class. You can specify conditions based on which the query will return matching records. <code>query.equalTo(&quot;owner&quot;, user);</code> means that I want to get all Venues which have the owner column set to <code>user</code>. Note that <code>user</code> is a pointer to the instance of the current user in the User class.<code>query.find()</code>  retrieves all the rows which satisfy the query and returns a promise.<br>
The <code>displayVenue</code> function is a utility function that takes as parameters the id of the div element inside which the venue cards will be displayed and a Venue object.</p>
<pre><code class="language-javascript">var i = 0; //iterator for colours in venue cards
const colours = [&quot;#8a068f&quot;, &quot;#06148f&quot;, &quot;#c70a62&quot;, &quot;#0a9956&quot;, &quot;#e78659&quot;, &quot;#87b40d&quot;, &quot;#0791b4&quot;, &quot;#8609ce&quot;, &quot;#4c7e80&quot;, &quot;#c2427e&quot;, &quot;#838080&quot;];

function displayVenue(displayArea, venue) {
    var venuediv = document.createElement(&quot;div&quot;);
    venuediv.className = &quot;venue col-md-6 col-lg-3 mb-4 d-flex align-items-stretch text-center&quot;;
    var photo = venue.get(&quot;image1&quot;).url();
    var objId = venue.id;

    venuediv.innerHTML =
        `&lt;div class='card' id='${objId}' onclick='venueDetails(this)' style ='border-bottom: 4px solid ${colours[i]};'&gt;
            &lt;img class='card-img-top' height='230px' src='${photo}'&gt;
            &lt;div class='card-body'&gt;
                &lt;h5 class='card-title'&gt;${venue.get(&quot;venueName&quot;)}&lt;/h5&gt;
                &lt;span class='tag tag-place'&gt;&lt;small class=&quot;capitalised&quot;&gt;${venue.get(&quot;city&quot;)}&lt;/small&gt;&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;`;
    displayArea.appendChild(venuediv);
}
</code></pre>
<p>The <code>.get</code> method of an object can be used to obtain the value of any field in it.</p>
<p>Notice that for the cards I'm assigning the id to the id of the venue object. So, when an user clicks on the card, its id would be added as a parameter to the url which would then be used to identify the venue whose details have to be displayed.</p>
<pre><code class="language-javascript">function venueDetails(el) {
    window.location.href = &quot;venue.html?id=&quot; + el.id;
}
</code></pre>
<p>Customer's dashboard:</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/sked_customerDashboard.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>The customer can check venues as well as their bookings by toggling the button in the navbar. In the html I've created empty containers for each of them.</p>
<pre><code class="language-html">&lt;body onload=&quot;showVenues(); getCustomerBookings();&quot;&gt;

&lt;nav&gt;
     &lt;button class=&quot;navbtns navlink&quot; id=&quot;toggle_btn&quot; 
onclick=&quot;showOther(this)&quot;&gt;Show Bookings&lt;/button&gt;
&lt;/nav&gt;
...

&lt;div id=&quot;showVenuesHomepg&quot; class=&quot;row&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;customerBookings&quot; class=&quot;row my-4 d-none&quot;&gt;&lt;/div&gt;
...

&lt;/body&gt;
</code></pre>
<pre><code class="language-javascript">//Toggle between showing venues and bookings
function showOther(el) {
    if (el.innerHTML == &quot;Show Venues&quot;) {
        el.innerHTML = &quot;Show Bookings&quot;;
        document.getElementById(&quot;customerBookings&quot;).classList.add(&quot;d-none&quot;);
        document.getElementById(&quot;venues&quot;).style.display = &quot;block&quot;;
    }
    else {
        el.innerHTML = &quot;Show Venues&quot;;
        document.getElementById(&quot;venues&quot;).style.display = &quot;none&quot;;
        document.getElementById(&quot;customerBookings&quot;).classList.remove(&quot;d-none&quot;);
    }
}

function showVenues() {
    const Venues = Parse.Object.extend(&quot;Venues&quot;);
    const query = new Parse.Query(Venues);
    query.limit(25);
    query.find().then(function success(results) {
        results.forEach((venue, index) =&gt; {
            const displayArea = document.getElementById(&quot;showVenuesHomepg&quot;);
            if (i == 11) { i = 0 };
            displayVenue(displayArea, venue);
            i += 1;
        });
    }, function error(err) {
        console.log(&quot;Error : &quot;, err);
    });
}
</code></pre>
<p>The showVenues function is quite similar to the getOwnerData function except that here I'm fetching the top 25 rows in the Venues class using <code>query.limit(25)</code>.By default though, parse returns the top 100 query results.</p>
<h3 id="filteringvenuesbasedonlocationanamechapter2a">Filtering venues based on location <a name="chapter-2"></a></h3>
<p>As the number of venues gets larger a function that can filter venues based on their location would be useful. Now, we'll see how this can be done.</p>
<p>In the customer.html page create an input field to let the user enter a city name.</p>
<pre><code class="language-html">&lt;div class=&quot;container&quot;&gt;
    &lt;h2 id=&quot;venHeading&quot; class=&quot;my-4 text-center&quot;&gt;VENUES&lt;/h2&gt;
    &lt;div class=&quot;input-group input-group-lg mb-4&quot;&gt;
        &lt;input type=&quot;text&quot; id=&quot;locationfilter&quot; name=&quot;venuesfilter&quot; class=&quot;form-control&quot; aria-label=&quot;searchBar&quot; aria-describedby=&quot;locationSearchBar&quot; placeholder=&quot;Enter a location..&quot;&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-secondary&quot; id=&quot;locationSearchBar&quot;
            onclick=&quot;filterVenues();&quot;&gt;Search&lt;/button&gt;
    &lt;/div&gt;
    &lt;div id=&quot;filterNoResults&quot; class=&quot;my-3 text-center text-danger&quot;&gt;&lt;/div&gt;
    &lt;div id=&quot;showVenuesHomepg&quot; class=&quot;row&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Earlier we made a query based on the <em>owner</em> column. Now, we're interested in querying based on the <em>city</em> column.</p>
<pre><code class="language-javascript">function filterVenues() {
    document.getElementById(&quot;filterNoResults&quot;).innerHTML = &quot;&quot;;
    var loc = document.getElementById(&quot;locationfilter&quot;).value.toLowerCase();
    const Venues = Parse.Object.extend(&quot;Venues&quot;);
    const query = new Parse.Query(Venues);
    query.equalTo(&quot;city&quot;, loc);
    query.find().then(function findVenues(results) {
        if (results.length == 0) {
            document.getElementById(&quot;filterNoResults&quot;).innerHTML = &quot;No venues found !&quot;;
        } else {
            const displayArea = document.getElementById(&quot;showVenuesHomepg&quot;);
            displayArea.textContent = &quot;&quot;; //Remove all venues so as to display only the filtered venues
            results.forEach((venue, index) =&gt; {
                if (i == 11) { i = 0; }
                displayVenue(displayArea, venue);
                i += 1;
            });
        }
    }, function error(err) {
        alert('Error : ', err.message);
    });
}
</code></pre>
<p>To make this query a little robust, while saving the city for the Venue object I had first converted it to lower case so that now we can convert the input field's value to lowercase and make the search case insensitive.</p>
<h3 id="displayvenuedetailsanamechapter3a">Display venue details <a name="chapter-3"></a></h3>
<p>When clicking on any of the venue card, the insertDetails function is triggered which shows a new page with information about the venue which contains:</p>
<ol>
<li>Images of the venue.</li>
<li>Details about location, timings, etc.</li>
<li>A calendar. If any date on this calendar is clicked, it shows the time slots that have already been booked so that customers can plan their event accordingly.</li>
<li>A form to send a booking request.</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/sked_VenueDetailsPage.gif" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>You can find the code for this page in the venue.html file where I have created empty containers for venue images, details, calendar and added a booking form. Here's a rough outline:</p>
<pre><code class="language-html">&lt;body onload=&quot;insertDetails(); getDates();&quot;&gt;
     &lt;div id=&quot;loader&quot; class=&quot;centered&quot;&gt;&lt;/div&gt;

    /* Insert navbar */

    &lt;div class=&quot;container my-4 whileLoadHide&quot;&gt;

        /* slideshow to show images of the venue */

        /* empty divs to show details like timings,address etc */

    &lt;/div&gt;

    &lt;div class=&quot;container my-4 whileLoadHide&quot; id=&quot;calholder&quot;&gt;
        
        /* Empty calendar */

    &lt;/div&gt;

    &lt;div id=&quot;bookVenue&quot; class=&quot;container mb-4 whileLoadHide&quot;&gt;
        
        /* Form to book venue */

    &lt;/div&gt;
&lt;/body&gt;
</code></pre>
<p>To get details about the venue from the database the function below is used:</p>
<pre><code class="language-javascript">var params, venueId,flag;

function insertDetails() {
    params = new URLSearchParams(location.search); //get the parameters in query string
    venueId = params.get('id');
    const Venue = Parse.Object.extend(&quot;Venues&quot;);
    const query = new Parse.Query(Venue);
    query.get(venueId).then((venue) =&gt; {
        // The object was retrieved successfully.
        document.getElementById(&quot;brand&quot;).innerHTML = venue.get(&quot;venueName&quot;);
        document.getElementById(&quot;img1container&quot;).innerHTML = `&lt;img class=&quot;d-block w-100&quot; src=&quot;${venue.get(&quot;image1&quot;).url()}&quot; alt=&quot;First Image&quot; style=&quot;max-height:720px&quot;&gt;`
        document.getElementById(&quot;img2container&quot;).innerHTML = `&lt;img class=&quot;d-block w-100&quot; src=&quot;${venue.get(&quot;image2&quot;).url()}&quot; alt=&quot;Second Image&quot; style=&quot;max-height:720px&quot;&gt;`
        document.getElementById(&quot;desc&quot;).innerHTML = venue.get(&quot;description&quot;);
        document.getElementById(&quot;city&quot;).innerHTML = venue.get(&quot;city&quot;);
        document.getElementById(&quot;address&quot;).innerHTML = venue.get(&quot;address&quot;);
        document.getElementById(&quot;days&quot;).innerHTML = venue.get(&quot;daysAvailable&quot;);
        document.getElementById(&quot;timing&quot;).innerHTML = venue.get(&quot;timings&quot;);

        var hiddencontent = document.getElementsByClassName(&quot;whileLoadHide&quot;);
        while (hiddencontent.length != 0) {
            hiddencontent[0].classList.remove(&quot;whileLoadHide&quot;);
        }
        document.getElementById(&quot;loader&quot;).style.display = &quot;none&quot;;

    }, (err) =&gt; {
        // The object could not be retrieved.
        alert(&quot;Error occured: &quot;, err.message);
        document.getElementById(&quot;loader&quot;).style.display = &quot;none&quot;;

    });
}
</code></pre>
<p>The <code>query.get</code> method can be used to find an object using its id.</p>
<p>The getDates function implements the calendar feature which you can easily build using HTML and CSS. Each date is shown as a button which on clicking calls the checkbooked function which checks the slots that are already booked. This depends on the booking functionality so I'll describe it later.</p>
<h3 id="sendingbookingrequesttoowneranamechapter4a">Sending booking request to owner <a name="chapter-4"></a></h3>
<p>At the bottom of the venue details page there is a form which any customer can fill to send a booking request.</p>
<pre><code class="language-html">&lt;form id=&quot;venueBookForm&quot;&gt;
    &lt;h4 style=&quot;color: #00a090;&quot;&gt;Book this venue&lt;/h4&gt;
    &lt;div class=&quot;row mb-3&quot;&gt;
        &lt;div class=&quot;col-md-6&quot;&gt;
            &lt;label for=&quot;custName&quot; class=&quot;form-label&quot;&gt;Full name&lt;/label&gt;
            &lt;input type=&quot;text&quot; id=&quot;custName&quot; name=&quot;customerName&quot; class=&quot;form-control&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;col-md-6&quot;&gt;
            &lt;label for=&quot;email&quot; class=&quot;form-label&quot;&gt;Email Id&lt;/label&gt;
            &lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;EmailId&quot; class=&quot;form-control&quot;&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    ...
    
    //Insert label and input fields to collect other details

    ...

    &lt;div class=&quot;my-2 text-danger&quot; id=&quot;bookingError&quot;&gt;&lt;/div&gt;
    &lt;button type=&quot;button&quot; onclick=&quot;bookVenue()&quot; class=&quot;btn text-light mb-2&quot; id=&quot;bookVenueBtn&quot;&gt;Book slot&lt;/button&gt;
    &lt;div class=&quot;my-2 text-success&quot; id=&quot;bookingSuccess&quot;&gt;&lt;/div&gt;
&lt;/form&gt;
</code></pre>
<pre><code class="language-javascript">function bookVenue() {
    document.getElementById(&quot;bookingError&quot;).innerHTML = &quot;&quot;;
    const name = document.getElementById(&quot;custName&quot;).value;
    const email = document.getElementById(&quot;email&quot;).value;
    const date = document.getElementById(&quot;date&quot;).value;
    const timeStart = document.getElementById(&quot;starttime&quot;).value
    const timeEnd = document.getElementById(&quot;endtime&quot;).value;
    const details = document.getElementById(&quot;purpose&quot;).value;

    if (!name || !email || !date || !timeStart || !timeEnd || !details) {
        document.getElementById(&quot;bookingError&quot;).innerHTML = &quot;Please fill all the fields.&quot;;
    }
    else {
        const user = Parse.User.current();

        const Venues = Parse.Object.extend(&quot;Venues&quot;);
        const q = new Parse.Query(Venues);
        q.get(venueId).then(function success(object) {
            var ownerOfVen = object.get(&quot;owner&quot;);

            const Booking = Parse.Object.extend(&quot;Booking&quot;);
            const booking = new Booking();

            var acl = new Parse.ACL();
            acl.setReadAccess(user, true);
            acl.setReadAccess(ownerOfVen, true);
            acl.setWriteAccess(ownerOfVen, true);

            booking.set(&quot;ACL&quot;, acl);
            booking.set(&quot;fullName&quot;, name);
            booking.set(&quot;email&quot;, email);
            booking.set(&quot;date&quot;, date);
            booking.set(&quot;timeSlot&quot;, timeStart + &quot; - &quot; + timeEnd);
            booking.set(&quot;details&quot;, details);
            booking.set(&quot;venue&quot;, object);
            booking.set(&quot;owner&quot;, ownerOfVen);
            booking.set(&quot;bookedBy&quot;, user);
            booking.set(&quot;approvedStatus&quot;, false);

            booking.save().then(function success(booking) {
                document.getElementById(&quot;venueBookForm&quot;).reset();
                document.getElementById(&quot;bookingSuccess&quot;).innerHTML = &quot;Booking done successfully!&quot;;
                console.log(&quot;Booking done!&quot;);
            }, function error(err) {
                console.log(&quot;Error: &quot;, err);
            });
        }, function error(err) {
            console.log(err);
        });
    }
}
</code></pre>
<p>The Booking class will be used to store details about booking requests. The approvedStatus field is a boolean value which if set to true, implies that the booking has been approved. As one of the field here is the email address of the customer who is booking the venue, we need to make sure that this data is private and can be read only by them and the owner. Also, the write access should be given only to the owner, as only they should be able to update the approvedStatus field.</p>
<p>But we'll have to show the timeSlots that have already been booked right? Yes, and to do so I have created another class named ApprovedBookings which contains only the venueId, timeslot, date, and a pointer to the Booking object and this class is publicly readable.</p>
<h3 id="approverequestsanamechapter5a">Approve requests <a name="chapter-5"></a></h3>
<p>In the owner's dashboard under the booking requests tab all the requests made to any of their venues would be displayed.<br>
Getting these booking details is very similar to how we fetched all the venues so I won't be going over it. Today's events tab is again the same thing but with a condition to find out only rows where date==today's date.</p>
<p>In the following image, the red box shows a request that hasn't been approved yet.<br>
<img src="https://media-blog.sashido.io/content/images/2021/08/sked_newBookingReq.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>On clicking the approve button the approvedStatus is set to true and a new row is added to the ApprovedBookings class.</p>
<pre><code class="language-javascript">function approveReq(el, id) {

    if (el.innerHTML == &quot;Approved&quot;) {
        return;
    }

    const Booking = Parse.Object.extend(&quot;Booking&quot;);
    const q = new Parse.Query(Booking);
    q.get(id).then((object) =&gt; {
        object.set(&quot;approvedStatus&quot;, true);
        object.save().then((booking) =&gt; {

            //create a row in ApprovedBookings class which has public read access
            const ApprovedBookings = Parse.Object.extend(&quot;ApprovedBookings&quot;);
            const approved = new ApprovedBookings();

            const acl = new Parse.ACL();
            acl.setPublicReadAccess(true);
            acl.setWriteAccess(Parse.User.current(), true);

            approved.set(&quot;date&quot;, booking.get(&quot;date&quot;));
            approved.set(&quot;timeSlot&quot;, booking.get(&quot;timeSlot&quot;));
            approved.set(&quot;venueID&quot;, booking.get(&quot;venue&quot;).id);
            approved.set(&quot;parent&quot;, object);
            approved.setACL(acl);
            approved.save().then(function () {
                console.log(&quot;approved and saved!&quot;);
            }, function error(err) {
                console.log(err);
            });

            el.innerHTML = &quot;Approved&quot;;
            el.classList.remove(&quot;cardpink-btn&quot;);
            el.classList.add(&quot;cardpurple-btn&quot;);
            const card = document.getElementById(id);
            card.classList.remove(&quot;cardpink-bg&quot;);
            card.classList.add(&quot;cardpurple-bg&quot;);
        }, function error(err) {
            console.log(err);
        });
    });
}
</code></pre>
<p>After approval:<br>
<img src="https://media-blog.sashido.io/content/images/2021/08/sked_approvedBooking.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>The time slots of approved bookings can be read by any user. Here's an example of how the slots already booked will appear upon clicking any date in the calendar.<br>
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2b46glbckeeohfco5de9.png" alt="How to Build a Venue Booking App with SashiDo - Part 2"></p>
<p>The main part of the checkbooked function which is called on clicking any date in the calendar is as follows:</p>
<pre><code class="language-javascript">const apprBooking = Parse.Object.extend(&quot;ApprovedBookings&quot;);
const query = new Parse.Query(apprBooking);
query.equalTo(&quot;venueID&quot;, venueId);
query.equalTo(&quot;date&quot;, datecheck);
query.find().then(successCallback,errorCallBack);
</code></pre>
<p>veneuId is a global variable containing the id of venue whose value was set in the insertDetails function.<em>date</em> refers to the date in the calendar which was clicked.</p>
<h3 id="deletingpastbookingsanamechapter6a">Deleting past bookings <a name="chapter-6"></a></h3>
<p>Once an event has completed, maybe the owner doesn't need the booking information anymore so we have to provide a delete option for expired bookings. We will have to destroy it from both the Booking class and ApprovedBookings class. As ApprovedBookings has a pointer to its parent in Bookings, we'll start by deleting it first.<br>
But it is possible that a booking was never approved. Then it would be present only in the Bookings class.</p>
<pre><code class="language-javascript">function deleteBooking(bookingid) {
    const Booking = Parse.Object.extend(&quot;Booking&quot;);
    const query = new Parse.Query(Booking);

    query.get(bookingid).then((bking) =&gt; {

        const status = bking.get(&quot;approvedStatus&quot;);

        //If approved,first remove record from ApprovedBookings class.
        if (status) {
            const apprBookings = Parse.Object.extend(&quot;ApprovedBookings&quot;);
            const q = new Parse.Query(apprBookings);
            q.equalTo(&quot;parent&quot;, bking);
            q.find().then((result) =&gt; {
                result[0].destroy().then(() =&gt; {
                    console.log(&quot;Deleted booking from ApprovedBookings&quot;);

                    //Next remove from Booking class
                    bking.destroy().then(() =&gt; {
                        const bookingcard = document.getElementById(bookingid);
                        bookingcard.parentElement.removeChild(bookingcard);
                        console.log(&quot;Deleted from Booking&quot;);
                    });
                });
            }, (err) =&gt; {
                console.log(err);
            });
        }
        else { //just remove the non approved booking from Booking class
            bking.destroy().then(() =&gt; {
                const bookingcard = document.getElementById(bookingid);
                bookingcard.parentElement.removeChild(bookingcard);
                console.log(&quot;Deleted from Booking&quot;);
            }, (err) =&gt; {
                console.log(err);
            });
        }
    });
}
</code></pre>
<h3 id="conclusionanamechapter7a">Conclusion <a name="chapter-7"></a></h3>
<p>I hope you've gotten some idea about how <a href="https://www.sashido.io/en/">SashiDo</a> can be used to perform various tasks in an easy way. This was a simple fun project so I haven't implemented any cloud code validation functions. Nevertheless, it was a great learning experience. There are a couple of more features that could have been added like allowing owners to edit venue details through the webpage itself and displaying time in the AM-PM format. I think it would be cool to have a feature that can show room usage statistics for each of the venues. Let me know in comments if you try any of those or add some of your own ideas!</p>
<p>Happy Coding!</p>
<h3 id="usefullinksanamechapter8a">Useful links <a name="chapter-8"></a></h3>
<p><a href="https://blog.sashido.io/venue-booking-system-part1">How to Build a Venue Booking App with SashiDo - Part 1</a><br>
<a href="https://github.com/nishkakotian/SKED">Github repo</a><br>
<a href="https://www.youtube.com/embed/CUpRyOhE7kM">Application Demo Video</a><br>
<a href="https://docs.parseplatform.org/js/guide/">Parse Javascript SDK documentation</a><br>
<a href="https://blog.sashido.io/sashidos-getting-started-guide">SashiDo's Getting Started Guide</a><br>
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">Arrow functions</a><br>
<a href="https://css-tricks.com/template-literals/">Template literals</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[How to Build a Venue Booking App with SashiDo - Part 1]]></title><description><![CDATA[Part 1 of a step-by-step guide on how to make your own venue booking web app using a NodeJS backend from SashiDo. The tutorial is perfect for beginners, who are looking for a fun project to start their JavaScript coding journey.]]></description><link>https://blog.sashido.io/venue-booking-system-part1/</link><guid isPermaLink="false">612b226cdcc2ad002048c507</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Project]]></category><dc:creator><![CDATA[Nishka Kotian]]></dc:creator><pubDate>Tue, 31 Aug 2021 12:00:04 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/08/cover-pin-2.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2021/08/cover-pin-2.png" alt="How to Build a Venue Booking App with SashiDo - Part 1"><p>A few months ago I got to know about Parse and SashiDo during a hackathon and I was very keen to build a full-stack web application using it. So, I decided to make a fun project by building a venue booking system. In the following lines I will describe how I went about coding it so you can follow allong the steps. A basic knowlege of Javascript would suffice to follow this tutorial.</p>
<p>To get an understanding of what comes next and how the application works, check out the demo video!</p>
<p><iframe width="854" height="480" src="https://www.youtube.com/embed/CUpRyOhE7kM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></p>
<h2 id="tableofcontents">Table Of Contents</h2>
<ul>
<li><a href="#chapter1">Project Overview</a></li>
<li><a href="#chapter2">Database</a></li>
<li><a href="#chapter3">User Registeration and Login</a></li>
<li><a href="#chapter4">Adding a new Venue</a></li>
<li><a href="#chapter5">Conclusion</a></li>
<li><a href="#chapter6">Useful links</a></li>
</ul>
<h3 id="projectoverviewanamechapter1a">Project overview <a name="chapter1"></a></h3>
<p>The venue booking application which I named SKED has two main types of users, venue owners and customers.</p>
<p>A customer can :</p>
<ul>
<li>Find details about venues and filter them based on location</li>
<li>Check time slots that have already been booked for any day</li>
<li>Send a booking request to the owner of that venue</li>
</ul>
<p>An owner can :</p>
<ul>
<li>Add a venue</li>
<li>Get bookings made for the present day</li>
<li>Approve booking requests made by customers</li>
<li>Delete bookings for events that have finished</li>
</ul>
<h3 id="databaseanamechapter2a">Database <a name="chapter2"></a></h3>
<p>Data is stored by creating a <code>Parse.Object</code> with key-value pairs. You can create classes to manage and store different kinds of data. When a class is created, it does not have a schema defined so you can add fields which can have any type of JSON compatible data. But once the first object is saved the data types for any fields that have been set will be locked. For example, if I create a class named <code>Venues</code> and my first object has a field <em>venueName</em> whose value I set to &quot;Auditorium&quot; then parse recognises that it was of string type. So whenever a new object is added it checks whether the venueName is of string type. If not, it returns an error.</p>
<p>Below is a diagram I created so that you can get a quick idea about the various classes and fields in them.</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/sked_db_classes.png" alt="How to Build a Venue Booking App with SashiDo - Part 1"></p>
<p>There is another class named <em>Role</em> created by default in parse, but since I did not create roles for this project I haven't shown it. Also, I did not put any thought into naming the classes 🤦‍♀️. I really should've named all of them in singular form and surely that would be a lesson learned for my next projects.</p>
<h3 id="userregisterationandloginanamechapter3a">User Registeration and Login <a name="chapter3"></a></h3>
<p>The <code>Parse.User</code> class has built in functions to handle and secure user account information. As there are two types of users I've added a new field called userType to differentiate between them. New fields can be added using the dashboard by clicking on the <em>Add a new column</em> button.</p>
<p>For the frontend, I've created two separate home pages for customers and owners as I wanted to display different messages on them. In each of them there are two buttons i.e. Register and Login. On clicking them a modal(bootstrap popup) opens up asking the user to enter the username and password. These two are mandatory.You could optionally ask for an email too.</p>
<pre><code class="language-html">&lt;form id=&quot;registerform&quot;&gt;    
    &lt;label for=&quot;username_reg&quot; class=&quot;form-label&quot;&gt;Username&lt;/label&gt;
    &lt;input id=&quot;username_reg&quot; name=&quot;username_r&quot; class=&quot;form-control mb-2&quot; type=&quot;text&quot;&gt;
    &lt;label for=&quot;pswd_reg&quot; class=&quot;form-label&quot;&gt;Password&lt;/label&gt;
     &lt;input id=&quot;pswd_reg&quot; class=&quot;form-control&quot; name=&quot;password_r&quot; type=&quot;password&quot;&gt;
    &lt;div id=&quot;regError&quot; class=&quot;mt-2 text-danger&quot;&gt;&lt;/div&gt;
    &lt;button type=&quot;button&quot; id=&quot;CustRegbtn&quot; onclick=&quot;register(this)&quot; class=&quot;btn bg-light-blue text-white my-2&quot;&gt;Register&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/sked_RegisterModal.png" alt="How to Build a Venue Booking App with SashiDo - Part 1"></p>
<p>I've created a similar form in the owner's homepage but with a different id for the Register button so as to be able to determine if the request was made by a customer or owner.</p>
<p>Function to register user:</p>
<pre><code class="language-javascript">function register(el) {
    const username = document.getElementById(&quot;username_reg&quot;).value;
    const password = document.getElementById(&quot;pswd_reg&quot;).value;

    var user_type;
    if (el.id == &quot;OwnerRegbtn&quot;) {
        user_type = &quot;Owner&quot;;
    }
    else {
        user_type = &quot;Customer&quot;;
    }

    if (username.length == 0) {
        document.getElementById(&quot;regError&quot;).innerHTML = &quot;Username cannot be empty&quot;;
    }
    else if (password.length &lt; 8 || password.length &gt; 16) {
        document.getElementById(&quot;regError&quot;).innerHTML = &quot;Password must be between 8-16 characters long&quot;;
    }
    else {
        const user = new Parse.User();
        user.set(&quot;username&quot;, username);
        user.set(&quot;password&quot;, password);
        user.set(&quot;userType&quot;, user_type);

        user.signUp().then(function success() {
            window.location.href = &quot;registered.html&quot;;
        }, function error(err) {
            document.getElementById(&quot;regError&quot;).innerHTML = err.message;
        });
    }
}
</code></pre>
<p><code>new Parse.User()</code> will create a new instance of the User class.After creating a new object, you can set the values for its fields using <code>.set</code> which takes two parameters, the name of the column and the value that you want to set it to. The <code>.signUp</code> method has to be used to register a new user. It is an asynchronous function, which returns a promise object which has a <code>.then</code> method that takes two functions for the success and error cases. One example of an error case is when the username is already taken by some other user.</p>
<p>Now, let's a look into the login part.</p>
<pre><code class="language-html">&lt;form id=&quot;loginform&quot;&gt;
    &lt;label for=&quot;username_login&quot; class=&quot;form-label&quot;&gt;Username&lt;/label&gt;
    &lt;input id=&quot;username_login&quot; name=&quot;username_l&quot; class=&quot;form-control mb-2&quot; type=&quot;text&quot;&gt;

    &lt;label for=&quot;pswd_login&quot; class=&quot;form-label&quot;&gt;Password&lt;/label&gt;
    &lt;input id=&quot;pswd_login&quot; class=&quot;form-control&quot; name=&quot;password_l&quot; type=&quot;password&quot;&gt;

    &lt;div id=&quot;loginError&quot; class=&quot;mt-2 text-danger&quot;&gt;&lt;/div&gt;
    &lt;button type=&quot;button&quot; id=&quot;CustLoginbtn&quot; onclick=&quot;login()&quot;
        class=&quot;btn bg-light-blue text-white my-2&quot;&gt;Login&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p>Function to login user:</p>
<pre><code class="language-javascript">function login() {
    var username = document.getElementById(&quot;username_login&quot;).value;
    var password = document.getElementById(&quot;pswd_login&quot;).value;

    if (username.length == 0) {
        document.getElementById(&quot;loginError&quot;).innerHTML = &quot;Please enter the username&quot;;
    }
    else if (password.length &lt; 8 || password.length &gt; 16) {
        document.getElementById(&quot;loginError&quot;).innerHTML = &quot;Passwords are between 8-16 characters long.&quot;;
    }
    else {
        Parse.User.logIn(username, password, { usePost: true }).then(function success() {
            const user = Parse.User.current();
            if (user.attributes.userType == &quot;Owner&quot;) {
                window.location.href = &quot;owner.html&quot;;
            }
            else { /*user.attributes.userType == &quot;Customer&quot;*/
                window.location.href = &quot;customer.html&quot;;
            }
        }, function error(err) {
            document.getElementById(&quot;loginError&quot;).innerHTML = err.message;
        });
    }
}
</code></pre>
<p>To log in an user, retrieve the input entered in the form and use the <code>.logIn method</code> by passing the username and password. By default it uses GET request but you can add an optional argument to tell Parse to use POST instead. Once the user is logged in, you can use <code>Parse.User.current()</code> to find out the current user. Then, using the attributes property of the object we can find the userType. Alternatively, the <em>.get</em> method can also be used like so - <code>user.get(&quot;userType&quot;)</code>.</p>
<p>If the login was successful the user will be taken to their dashboard (owner.html or customer.html) which has all their data.</p>
<p>To provide log out functionality use the <code>.logOut()</code> method.</p>
<pre><code class="language-html">/*Passing a boolean value true for owner &amp; false for customer 
so that after logout they can be taken to their respective home page */

&lt;button class=&quot;btn nav-link btn-link&quot; onclick=&quot;logout(true)&quot;&gt;Sign out&lt;/button&gt; //in owner.html

&lt;button class=&quot;btn nav-link btn-link&quot; onclick=&quot;logout(false)&quot;&gt;Sign out&lt;/button&gt; //in customer.html
</code></pre>
<pre><code class="language-javascript">function logout(isOwner) {
    Parse.User.logOut().then(function gotohome() {
        if (isOwner) {
            window.location.href = &quot;home.html&quot;;
        }
        else {
            window.location.href = &quot;home_customer.html&quot;;
        }
    });
}
</code></pre>
<p>Next, we'll be looking into how owners can add a new venue.</p>
<h3 id="addingavenueanamechapter4a">Adding a venue <a name="chapter4"></a></h3>
<p>I have created a class named <code>Venues</code> to store venue details. This can be done using the dashboard. Just click on the <em>Create a class</em> button and add all the required columns by clicking on <em>Add a new column</em>. You'll have to give the column a name and specify the type of data you want to store.</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/addven.png" alt="How to Build a Venue Booking App with SashiDo - Part 1"></p>
<p>If you remember, I mentioned that Parse will automatically find the data type from the first object that gets stored and now I'm asking you to specify the data type. What's happening here?</p>
<p>Well, creating a class beforehand isn't really necessary in Parse. If you create a subclass using Parse.Object.extend(&quot;class&quot;); and if that class did not exist Parse will create it for you. To enable this feature you'll have to go to <a href="https://dashboard.sashido.io/">SashiDo Dashbaord</a> &gt; App Settings &gt; Security and keys &gt; App permissions and enable client class creation. You can utilise it all through development and disable it before moving to production.</p>
<p>In the owner's dashboard there is a <em>Add a venue</em> button which on clicking opens up a form in which details about the venue must be entered.</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/08/sked_AddVenueForm.png" alt="How to Build a Venue Booking App with SashiDo - Part 1"></p>
<p>Here's an outline of the code for the form:</p>
<pre><code class="language-html">&lt;form&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
        &lt;label for=&quot;nameOfVenue&quot; class=&quot;form-label&quot;&gt;Name of the venue&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;nameOfVenue&quot; name=&quot;venueName&quot; class=&quot;form-control&quot;&gt;
    &lt;/div&gt;

    ...

    /* Insert label and input fields for all other details here*/

    ...

    &lt;div id=&quot;addVenueError&quot; class=&quot;mb-3 text-danger&quot;&gt;&lt;/div&gt;
    &lt;button type=&quot;button&quot; onclick=&quot;createVenue()&quot; id=&quot;venueSubmitBtn&quot;
        class=&quot;btn text-light mb-3&quot;&gt;Submit&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p>Once the submit button is clicked, the createVenue function shown below creates a new Venue object containing all the details that were entered. The venue will then show up in the owner's dashboard and also be visible to customers.</p>
<pre><code class="language-javascript">function createVenue() {
    document.getElementById(&quot;addVenueError&quot;).innerHTML = &quot;&quot;;
    const venuename = document.getElementById(&quot;nameOfVenue&quot;).value;
    const address = document.getElementById(&quot;addr&quot;).value;
    const city = document.getElementById(&quot;cityName&quot;).value.toLowerCase();
    const daysAvailable = document.getElementById(&quot;days&quot;).value;
    const topen = document.getElementById(&quot;topen&quot;).value; /*Venue opening time*/
    const tclose = document.getElementById(&quot;tclose&quot;).value; /*Venue closing time*/
    const timing = topen + &quot;-&quot; + tclose;

    const image1 = document.getElementById(&quot;image1&quot;);
    const image2 = document.getElementById(&quot;image2&quot;);

    const desc = document.getElementById(&quot;desc&quot;).value;

    //Client side validation to check that all fields are entered
    if (!venuename || !address || !city || !daysAvailable || !topen || !tclose || image1.files.length == 0 || image2.files.length == 0 || !desc) {
        document.getElementById(&quot;addVenueError&quot;).innerHTML = &quot;Please fill all the fields.&quot;;
    }
    else {
        const parseFileImg1 = new Parse.File(&quot;img1.jpeg&quot;, image1.files[0]);
        const parseFileImg2 = new Parse.File(&quot;img2.jpeg&quot;, image2.files[0]);

        const owner = Parse.User.current();
        /*create a subclass of the Venues class ie. inherit the properties of the Venues class.*/
        const Venue = Parse.Object.extend(&quot;Venues&quot;);
        //create an instance of the Venues class
        const venue = new Venue();

        var acl = new Parse.ACL();
        acl.setPublicReadAccess(true);
        acl.setWriteAccess(owner.id, true);

        venue.setACL(acl);
        venue.set(&quot;owner&quot;, owner); //pointer to owner
        venue.set(&quot;venueName&quot;, venuename);
        venue.set(&quot;address&quot;, address);
        venue.set(&quot;city&quot;, city);
        venue.set(&quot;daysAvailable&quot;, daysAvailable);
        venue.set(&quot;timings&quot;, timing);
        venue.set(&quot;image1&quot;, parseFileImg1);
        venue.set(&quot;image2&quot;, parseFileImg2);
        venue.set(&quot;description&quot;, desc);

        venue.save().then(function success(venue) {
            const displayArea = document.getElementById(&quot;displayVenues&quot;);
            displayVenue(displayArea, venue);
            i += 1;
            if (i == 11) { i = 0; }
            location.reload();
        }, function error(err) {
            alert(&quot;Error adding venue : &quot; + err.message);
        });
    }

};
</code></pre>
<p>Let's go over what this function is doing. First, I'm retrieving the values entered in the form and checking that no fields were left empty. Then, the images are stored in a <code>Parse.File</code> object which allows storing data that is too large to fit inside a normal <code>Parse.Object</code>. Finally, after setting the fields to their values the <code>.save()</code> method is used to save the object to the database and like I stated before, if the <em>Venues</em> class did not exist Parse will first create it and then save the object. The displayVenue function will just add a new card to display the venue in the owner's dashboard. More about this function can be found in <a href="https://blog.sashido.io/building-a-venue-booking-system-part-2">Part-2 of the tutorial</a>.</p>
<p>One important point to note here is that we need to make sure that only the owner can modify or delete the venue. In order to provide such fine grained security we need to set an <code>ACL</code> (ACL = Access control list) using which we can specify who have permissions to read or write to that particular object. <strong>setpublicReadAccess(true)</strong> as the name suggests means that <em>any</em> user can read that object and <strong>setWriteAccess(owner.id, true)</strong> implies that only the owner has write access. The boolean value <em>true</em> specifies that I want to give permission. If instead I wanted to deny access to a user, then I would set that parameter to false.</p>
<h3 id="conclusionanamechapter5a">Conclusion <a name="chapter5"></a></h3>
<p>So far, we have looked at user authentication and adding a new venue. If you would like to learn about querying the database and adding the booking functionality please check <a href="https://blog.sashido.io/building-a-venue-booking-system-part-2">Part-2 of the tutorial</a>.</p>
<h3 id="usefullinksanamechapter6a">Useful links <a name="chapter6"></a></h3>
<p><a href="https://blog.sashido.io/building-a-venue-booking-system-part-2">How to Build a Venue Booking App with SashiDo - Part 2</a><br>
<a href="https://github.com/nishkakotian/SKED">Github repo</a><br>
<a href="https://www.youtube.com/embed/CUpRyOhE7kM">Application Demo Video</a><br>
<a href="https://docs.parseplatform.org/js/guide/">Parse Javascript SDK documentation</a><br>
<a href="https://blog.sashido.io/sashidos-getting-started-guide">SashiDo's Getting Started Guide</a></p>
</div>]]></content:encoded></item></channel></rss>