menu

Building a Bus Booking App with InfoLobby: A Comprehensive Guide


Introduction

Welcome to the ultimate guide on building an advanced travel booking application using InfoLobby! In this comprehensive series, I'll walk you through every step of harnessing the powerful features of InfoLobby to create a robust bus booking system tailored for Kingdom Tours and Travels. Whether you're new to InfoLobby or looking to deepen your understanding, this guide will provide detailed insights into each stage of development.

The app will encompass the following features:

  • Customer Management: Create and store customer information. Automatically send welcome email.
  • Bus Management: Add and manage details of various buses in the fleet, and seat layouts including a visual seat chart.
  • Trip Creation: Schedule trips with specific dates, destinations, and buses.
  • Seat Booking: Allow users to visually select and book seats for a trip.
  • Passenger Manifest Generation: Generate downloadable PDF reports containing passenger details for each trip.

Table of Contents

  1. Create a new space
  2. Customers table
  3. Automated welcome email
  4. Bus Table
  5. Seat Layout Table
  6. Automatic Seat Generation
  7. Calculate Sum of Seats in a Bus
  8. Add Seat Layout as Subtable in Bus Item
  9. Build Visual Seat Chart
  10. Trips & Bookings Table
  11. Add Bookings as Subtable in Trips table
  12. Automatic Price and Seat Calculation on Booking
  13. Calculate Available Seats and Total Revenue from a Trip
  14. Create Visual Seat Selection Tool
  15. Create Passenger Manifest
  16. Create Bookings subtable in Customers Table

Navigating the InfoLobby Interface

As you log into InfoLobby, you'll find a user-friendly interface designed to simplify data management. On the left-hand side, you'll see a list of accounts you're associated with. Click on an account to explore the spaces within it. For our project with Kingdom Tours and Travels, we'll begin by creating a dedicated space to manage all bus booking operations.

Creating a New Space

  • Navigate to the account.
  • Click on "New Space" on the account page or navigate through the sidebar by clicking the plus icon next to the account name.
  • Name this space "Kingdom Tours & Travels".

This space will serve as the centralized hub for organizing and managing data related to bus bookings.

Create Customers Table

  • Create a New Table
    • Navigate to the workspace.
    • Click "New Table" to initiate the creation of a bus management table.
    • Name this table "Customers".
  • Define Customer details
    • Name (Text Field)
    • Phone Number (Text Field)
    • Email (Text Field)
    • Address (Text Area)

Populating Data

Once the customer information table is set up, it's time to populate it with sample data. You can manually add individual customer details and explore InfoLobby's bulk import feature for larger datasets.

  • Navigate to your table's settings.
  • Select "Import Excel" and choose the file containing your customer data.
  • InfoLobby will prompt you to match fields from your Excel spreadsheet to corresponding fields in your table
  • Once matched, click "Import" to start the process.

Creating Automated Welcome Emails in InfoLobby

Next, we are going to set up an automation in InfoLobby to send a welcome email every time a new customer is created.

  • Navigate to your table's settings.
  • Look for the "Automations" tab and click on it.
  • Click on "New Flow" button.
  • Name your automation flow "Send welcome email."
  • Choose "When a record is created" as the trigger for this automation.
  • Leave the conditions section blank as we want the email to be sent for every new customer without specific conditions.
  • In the "Then" section, add "Send an Email" as the action. Configure the email details:
    • From Email: Set the sender's email address for the welcome email.
    • To Email: Use dynamic tokens to select the recipient's email. Type @ to see all the variable tokens and choose This Record: Email/@{current:email} field to automatically fill in the customer's email.
    • Subject: Write "Welcome to Kingdom Travels" as the subject line.
    • Body: Customize the email content using tokens. Again type @ to see all the variable tokens and choose This Record: Name/@{current:name} to automatically fill in the customer's name. Personalize the message with a welcoming note, details about Kingdom Tours & Travels, and a call-to-action. Use the rich text editor to format the email, add colors, links, and other visual elements to enhance its appeal.
  • Use the eye icon at the bottom to preview how the email will appear to recipients. Once satisfied, save the automation setup.

Testing

  • Create a new customer record to test the automation.
  • Verify that the email is automatically sent to the customer's email address once the record is created.
  • InfoLobby automatically logs all sent emails as comments within the details of the respective customer record, ensuring a clear record of communication history.
  • InfoLobby automation enhances efficiency by helping you to automate routine tasks and improve customer engagement through timely and personalized communications.

Bus Management

Following up on creating a customer table and email automation, this section dives into managing your bus fleet within InfoLobby. Here, we'll establish a well-organized system for tracking buses and their seating arrangements.

Setting Up the Bus Table

  • Create a New Table
    • Navigate to the workspace.
    • Click "New Table" to initiate the creation of a bus management table.
    • Name this table "Bus".
  • Define Customer details
    • Bus Name (Text field)
    • Model (Text field)
    • License Plate Number (Text field)

Setting Up the Seat Layout Table

  • Create a new table. Call it "Seat Layout".
  • Define Fields to Include:
    • Bus (Relationship Field): Set up a relationship with the "Bus" table.
    • Seat (Text field): Enter the seat number (e.g., 1A, 1B, etc.).
    • Price (Number field): Enter the price of the seat.

Creating Automation to Generate Bus Layout

  • Navigate to your table's settings.
  • Look for the "Automations" tab and click on it.
  • Click on "New Flow" button.
  • Name your automation flow "Generate Seat Layout".
  • Choose "When a record is created" as the trigger for this automation.
  • This means whenever a new bus record is added to the system, the automation will automatically run.
  • Leave the conditions section blank as we want the automation to run unconditionally.

Automation Action (THEN Section)

    • Custom Variable Block:
      • Name: seat_layout
      • Value:
        $layout = [];
        for ($x = 1; $x <= 11; $x++) {
        	array_push($layout,['title'=> $x."A", 'price'=> 250]);
        	array_push($layout,['title'=> $x."B", 'price'=> 150]);
        	array_push($layout,['title'=> $x."C", 'price'=> 150]);
        	array_push($layout,['title'=> $x."D", 'price'=> 250]);
        }
        return $layout;
      • Note: We create an array of seat name and price representing the seat chart.
    • For Each Variable Block:
      • Name: seat_layout
      • Select the seat_layout variable to loop through it.
      • This loop iterates over each element in the seat_layout variable.
      • Within the loop, a new variable named seat_layout_loop becomes available. It represents the current item being processed.
      • We will extract data from seat_layout_loop and create a new seat layout record.
      • Custom Variable Block:
        • Name: seat
        • Value: Variable: seat_layout_loop["title"] / @{var:seat-layout-loop}["title"]
        • Note: This extracts the seat title from the current item and stores it in the seat variable.
      • Custom Variable Block:
        • Name: price
        • Value: Variable: seat_layout_loop["price"] / @{var:seat-layout-loop}["price"]
        • Note:This extracts the seat price from the current item and stores it in the price variable.
      • Create New Record Block:
        • Bus: This Record: ID / @{current:item_id}
        • Seat: Variable: seat / @{var:seat}
        • Price: Variable: price / @{var:price}
        • Note: This creates a new seat layout record using the extracted seat title and price.
  • Save the Automation.

Simulation and Testing

To test the automation, create a new sample bus record. Once created, you should see that the automation has run and 44 new seat records are created in the "Seat Layout" table.

Creating Total Seats Available Field

  • Navigate to the Bus table settings.
  • Add a new calculation field named "Total Seats Available".
  • In the calculation formula field, type @. A list of token variables will appear, including ready-made calculations such as min, max, sum, count, average, etc for incoming relationships.
  • Select Seat Layout: Related Count / @{2252:item_count}. This counts all the Seat Layout records related to this table.
  • Save the changes.
  • Now, in the Bus table, the "Total Seats Available" field will display the total count of seats. For example, if there are 44 seats, it will show 44.

Creating a Subtable for Related Seats from Bus Layout

Currently, viewing all the seats for a bus requires navigating to the separate seat table and filtering for that specific bus. To improve this workflow, we can create a subtable within the bus table itself. This subtable would display all the seats associated with a particular bus, allowing you to see them conveniently in one place.

Setting Up the Subtable

  • Navigate to Table Settings
  • Within the table settings, look for a tab labeled "Tab Groups". This section allows you to configure additional subtables within your main "Bus" table. Select the "Tab Groups" Tab
  • At the bottom right of the "Tab Groups" section, you'll likely find a list of available incoming relationships.
  • Click the relationship named "Seat Layout" from the list. This signifies the connection between the "Bus" table and the "Seat Layout" table. Selecting this establishes the basis for the subtable.
  • Once you choose the "Seat Layout" relationship, a subtable will be automatically created. Select the specific fields you want to display in the subtable. In this case, choose the following fields:
    • Seat: This field should represent the seat identifier (e.g., "1A", "2B").
    • Price:This field should display the price associated with each seat.
  • Once you've selected the desired fields, confirm and save your configuration. Now, when you open a bus record, you'll see a new tab with a subtable displaying all the seats associated with that bus. Voila, InfoLobby makes it all a breeze.

Creating a Visual Bus Seat Layout Using the Powerful Script Field from InfoLobby

Next, let me walk you through creating a visual representation of your bus seat layout within InfoLobby. We'll leverage InfoLobby's "Script" field type and PHP scripting to dynamically generate the HTML code for the seat layout.

Go to "Table Settings" and Add a new Script Field. Call it "Seat Layout".

Since there won't be any seats defined when creating a new bus record, it's best to keep this subtable hidden initially. You can achieve this by checking the "Hide on Create" option.

Gathering Seat Data

InfoLobby offers a script field type, allowing you to display dynamic calculations. This field uses "Lobby Script," which is essentially PHP with additional helper functions.

We'll use the il_loop_related function to retrieve all related seats associated with a specific bus. This function takes several arguments. You can start typing il and all the il functions will be listed to you to click from.

  • Current Table: ID /@{int:current-table-id}: ID of the current table (the bus table in our case).
  • Current Record: ID /@{int:current-record-id}: ID of the current record (the specific bus record).
  • 2252: ID of the related table containing seat layout data (Go to Seat Layout Table > Table Settings > Developer Tab to get the id ).
  • "item_id": Field name used for sorting (typically the seat ID).
  • "asc": Sorting order (ascending in this case).

Building the Basic HTML Structure

We'll create a variable named $bus_html to store the HTML code for the bus layout. We'll start by opening a div element with the class bus-div. This div will contain the entire bus layout.

Inside the bus-div, we'll iterate through each retrieved seat using a foreach loop. For each seat, we'll create another div element with the class seat. This div will represent an individual seat. Within the seat div, we'll display the seat identifier using the $seat['seat'] variable.

$bus_html = "<div class='bus-div'>";
foreach ($seat_layout as $seat ){
    $bus_html .= "<div class='seat'>". $seat['seat'] ."</div>";
}
$bus_html .= "<div>";

Adding Aisle Gaps

To visually separate seat rows, we'll introduce aisle gaps after every two seats. We'll introduce a counter variable $seat_count to track the number of seats processed so far. Inside the loop, we'll check if $seat_count % 4 == 2. This condition ensures we add an aisle gap after every two seats (because 4 modulo 2 is always 0). When the condition is met, we'll create another div element with the class aisle. This empty div will represent the gap between seats.

$bus_html = "<div class='bus-div'>";
$seat_count = 0;
foreach ($seat_layout as $seat ){
    $bus_html .= "<div class='seat'>". $seat['seat'] ."</div>";
    $seat_count++;
    if($seat_count % 4 == 2){
        $bus_html .= "<div class='isle'></div>";
    }
}
$bus_html .= "<div>";

Styling the Layout (Optional)

While the basic structure provides a functional layout, you can enhance its visual appeal using CSS styles. We'll define styles within a <style> tag using triple quotes ("""). You can customize these styles to match your preferences:

$bus_html .= """ 
<style>
.bus-div {
    width: 250px; 
    display: flex; 
    flex-wrap: wrap;
    border: 1px solid #ccc; 
    padding: 10px; 
    border-radius: 5px; 
}
.isle {
    width: 20px;
}
.seat {
    display: inline-block; 
    width: 40px; 
    height: 40px; 
    margin: 5px; 
    border: 1px solid #000; 
    background-color: grey; 
    color: #fff; 
    text-align: center; 
    line-height: 40px; 
    border-radius: 5px; 
    cursor: pointer;
}
</style>
""";

These styles define properties for the bus-div, aisle, and seat classes, controlling their size, layout, and appearance.

Putting it All Together

After defining the HTML structure and styles, we'll use the return statement to return the complete $bus_html variable. This variable contains the final HTML code for the bus layout.

Full Code

$seat_layout = il_loop_related( @{int:current-table-id},@{int:current-record-id},2252,"item_id","asc");

$bus_html = "<div class='bus-div'>";
$seat_count = 0;
foreach ($seat_layout as $seat ){
    $bus_html .= "<div class='seat'>". $seat['seat'] ."</div>";
    $seat_count++;
    if($seat_count % 4 == 2){
        $bus_html .= "<div class='isle'></div>";
    }
}

$bus_html .= "<div>";

$bus_html .= """ 
<style>
.bus-div {
    width: 250px; 
    display: flex; 
    flex-wrap: wrap;
    border: 1px solid #ccc; 
    padding: 10px; 
    border-radius: 5px; 
}
.isle {
    width: 20px;
}
.seat {
    display: inline-block; 
    width: 40px; 
    height: 40px; 
    margin: 5px; 
    border: 1px solid #000; 
    background-color: grey; 
    color: #fff; 
    text-align: center; 
    line-height: 40px; 
    border-radius: 5px; 
    cursor: pointer;
}
</style>
""";

return $bus_html;

Now, whenever you view a specific bus record, the script will dynamically generate the HTML code for the bus layout, displaying the seat arrangement within the InfoLobby interface. You can further customize the styles to achieve your desired visual representation.

Creating Trips and Bookings Tables

The Trips table will store information about each trip offered. Here's how to set it up:

  • Create a new table. Call it "Trips".
  • Define Fields to Include:
    • Title (Text field)
    • Date (Date field)
    • Destination (Text field)
    • Bus (Relationship field) Set up a relationship with the "Bus" table.
  • Create another new table. Call it "Bookings".
  • Define Fields to Include:
    • Customer (Relationship field) Set up a relationship with the "Customers" table.
    • Trip (Relationship field) Set up a relationship with the "Trips" table.
    • Seats (Text field)
    • Seat Count (Number field)
    • Total Amount (Number field)

Add the Bookings Table as a Subtable Inside the Trips Table

Now, you'll want to see a list of bookings associated with each trip for easy management. Here's how to achieve that:

  • Go to the table settings for the Trips table.
  • Navigate to the Tab Groups section.
  • In the "Incoming Relationships" list, select the "Bookings" relationship.
  • Choose the fields you want to display in the subtable (e.g., Customer name, Seats, Number, Total Amount).
  • Save the changes.

Creating Automation to Calculate Seat Count and Total Cost on Each Booking

  • Navigate to your table's settings.
  • Look for the "Automations" tab and click on it.
  • Click on "New Flow" button.
  • Name your automation flow "Calculate Booking Cost".
  • Choose "When a record is created" as the trigger for this automation.
  • This means whenever a new bus record is added to the system, the automation will automatically run.
  • Leave the conditions section blank as we want the automation to run unconditionally.

Automation Action (THEN Section)

  • Custom Variable Block:
    • Name: seats
    • Value: explode(",", This Record: Seats / @{current:seats})
    • Note: The "Seats" field can contain multiple seat names separated by commas. To handle this, we use the PHP function explode to split the comma-separated string into an array of individual seat names.
  • Custom Variable Block:
    • Name: trip_id
    • Value: This Record: Trip["id"] / @{current:trip}["id"]
    • Note: This extracts the Trip ID from the current record.
  • Custom Query Block: Get Bus Details from Trips Table
    • Name: bus
    • From Table: Trips
    • Select Field: bus
    • Where Field: item_id = '@{var:trip_id}'
    • Order By: Leave empty
    • Limit: 1
  • Custom Variable Block:
    • Name: bus_id
    • Value: Variable: bus[0]["bus"]["id"] / @{var:bus}[0]["bus"]["id"]
    • Note: Extract the Bus ID from the previously retrieved bus details.
  • Custom Variable Block:
    • Name: total
    • Value: 0
    • Note: Initialize the total variable to 0.
  • For Each Variable Loop Block
  • Select the seats variable to loop through it.
  • This loop iterates over each element in the seats variable.
  • Within the loop, a new variable named seats_loop becomes available. It represents the current item being processed.
  • We will query "Seat Layout" table using the seat name which is available in seats_loop.
    • Name: price
    • From Table: Seat Layout
    • Select Field: price
    • Where Field: bus = @{var:bus_id} AND seat = @{var:seats-loop}
    • Order By: Leave empty
    • Limit: 1
    • Custom Variable Block:
        • Name: total
        • Value: @{var:total} + @{var:price}[0]["price"]
        • Note: Update the total value by adding the price of the current seat.
  • Custom Variable Block:
      • Name: seat_count
      • Value: @{var:total} + @{var:price}[0]["price"]
      • Note: Update the total value by adding the price of the current seat.
  • Update Record Block:
      • Record: This Bookings Record
      • Total Amount: @{var:total}
      • Seat Count: @{var:seat_count}

This automation ensures that whenever a new booking record is created, the seat count and total cost fields are automatically populated. This is achieved by:

  • Extracting and processing the seats field.
  • Retrieving the bus details from the trip associated with the booking.
  • Calculating the total price of the seats.
  • Counting the number of seats.
  • Updating the booking record with the calculated seat count and total cost.

Calculating Total Seats Available and Total Revenue for a Trip

Next, let us automate the calculation of the total seats available and total revenue for each trip.

Available Seats Calculation

  • Navigate to the Trips table settings.
  • Add a new calculation field named "Available Seats".
  • In the calculation formula field, type @. A list of token variables will appear, including ready-made calculations such as min, max, sum, count, average, etc for incoming relationships.
  • Use the formula @{2251:total-seats-available} - @{2301:sum:seat-count}.
  • This formula gets the "Total Seats Available" field from the bus table (@{2251:total-seats-available}) and subtracts the sum of the seat counts from the bookings table (@{2301:sum:seat-count}).
  • Save the changes.

Total Revenue Calculation

  • Add another calculation field named "Total Revenue".
  • Use the formula @{2301:sum:total-amount}.
  • This formula calculates the total revenue by summing up the total amount from all bookings related to the trip (@{2301:sum:total-amount}).
  • Save the changes.

Building Visual Seat Layout and Seat Selector Using Script Field

Having established the core functionalities, let's move on to building a visual seat selection tool similar to those found on airplane booking websites. This will allow users to easily select seats from a visual layout, view their corresponding prices, and proceed with booking directly.

Here's how we'll achieve this:

  • Leverage InfoLobby's powerful script field: We'll utilize this feature to create a dynamic and interactive seat selection widget.
  • Build upon the existing seat layout: The seat data we defined within the bus table will serve as the foundation for the visual seat selection tool.

This enhancement will significantly improve the user experience by offering an intuitive and interactive way to choose and book seats.

Go to "Table Settings" and Add a new Script Field. Call it "Seat Selector".

Initial Setup and Fetching Trip Details

$trip = @{var:current-record}["data"]["trip"];
$trip_id = $trip["id"];

if(!$trip_id) {
    return "#";
}

$trip = il_record_get(2300,$trip_id);
$bus_id = $trip["data"]["bus"]["id"];
  • Purpose: This section retrieves the current trip details.
  • Steps:
    • Fetches the current record and extracts the trip_id.
    • If no trip_id is found, the script exits.
    • Retrieves the trip record using il_record_get and extracts the bus_id.

Fetching Taken Seats

$taken_seats = [];
    $ts_filter = [{"field":"trip","compare":"EQ","value":$trip_id}];
    $taken_seats_loop = il_loop_filter(2301,$ts_filter);
    foreach ($taken_seats_loop as $taken_seats_loop_item){
        $seats = explode(",",$taken_seats_loop_item["seats"]);
        foreach($seats as $seat){
            array_push ($taken_seats, $seat);
        }
    }
  • Purpose: To gather all seats that are already booked for the current trip.
  • Steps:
    • Initializes an empty array $taken_seats.
    • Sets up a filter to find bookings related to the current trip.
    • Loops through the bookings, extracts the seats, and adds them to the $taken_seats array.

Fetching Available Seats

$available_seats = [];
    $available_seats_with_price = [];
    $as_filter = [{"field":"bus","compare":"EQ","value":$bus_id}];
    $avl_seats_loop = il_loop_filter(2252,$as_filter);
    foreach ($avl_seats_loop as $avl_seats_loop_item){
        if(!in_array($avl_seats_loop_item["seat"],$taken_seats)){
            array_push ($available_seats, $avl_seats_loop_item["seat"]);
            array_push ($available_seats_with_price,[
                $avl_seats_loop_item["seat"],$avl_seats_loop_item["price"]
            ]);
        }
    }
    
  • Purpose: To gather all seats that are available for booking.
  • Steps:
    • Initializes arrays $available_seats and $available_seats_with_price.
    • Sets up a filter to find seats related to the bus.
    • Loops through the seats and adds them to the arrays if they are not already taken.
    • To simplify building the seat layout later, we've created two separate arrays. $available_seats contains just the names of the available seats. $available_seats_with_price holds the corresponding prices for each seat. The price will be displayed when a user selects that particular seat.

Building the HTML Seat Layout

$seat_layout = il_loop_related(2251,$bus_id,2252,"item_id","asc");

    $bus_html = "<div class='bus-div' id='busLayout'>";
    $seat_count = 0;
    foreach ($seat_layout as $seat ){
        $seat = $seat['seat'];
        $color = in_array($seat,$taken_seats) ? 'red' : (in_array($seat,$available_seats) ? 'green': 'grey');
        $cursor = in_array($seat,$taken_seats) ? 'not-allowed' : (in_array($seat,$available_seats) ? 'pointer': '');
        $price = 0;
        if(in_array($seat,$available_seats)){
            $array_col = array_column($available_seats_with_price,1,0);
            $price = $array_col[$seat];
        }
        $bus_html .= "<div class='seat' data-seat='$seat' data-price='$price' style='display: inline-block; width: 40px; height: 40px; margin: 5px; border: 1px solid #000; background-color: $color; color: #fff; text-align: center; line-height: 40px; border-radius: 5px; cursor: $cursor;'>". $seat ."</div>";
        $seat_count++;
        if($seat_count % 4 == 2){
            $bus_html .= "<div class='isle'></div>";
        }
    }
    $bus_html .= "</div>";
    
  • Purpose: To create the visual layout of the seats.
  • Steps:
    • Retrieves the seat layout related to the bus.
    • Initializes the HTML container for the bus layout.
    • Loops through the seats and assigns colors and cursor styles based on their availability status.
    • Sets background colors: Red for booked, green for available, grey for others.
    • Sets cursor styles: "not-allowed" for booked (disallowing clicks), "pointer" for available (indicating clickable).
    • Generates the seat divs with appropriate data attributes and styles.

Adding the Footer

$bus_html .= "<div id='footer' style='position: sticky; bottom:0; background-color: #f8f8f8; padding: 10px; border-top: 1px solid #ddd; margin-top: 10px; '>";
$bus_html .= "<div id='selectedSeats' style='margin-bottom: 10px;'></div>";
$bus_html .= "<div id='totalAmount' style='margin-bottom: 10px;'></div>";
$bus_html .= "<button id='clearSelections'>Clear Selections</button>";
$bus_html .= "<button id='clearSeatField'>Clear Seat Field</button>";
$bus_html .= "</div>";
  • Purpose: To create a footer section for displaying selected seats and the total amount.
  • Steps:
    • Adds a sticky footer to the HTML with containers for displaying selected seats and total amount.
    • Includes buttons for clearing selections and clearing the seat field.

Adding Styles and Scripts

$bus_html .= """ 
<style>
.bus-div {
    width: 250px; 
    display: flex; 
    flex-wrap: wrap;
    border: 1px solid #ccc; 
    padding: 10px; 
    border-radius: 5px; 
}
.isle {
    width: 20px;
}
.seat {
    display: inline-block; 
    width: 40px; 
    height: 40px; 
    margin: 5px; 
    border: 1px solid #000; 
    background-color: grey; 
    color: #fff; 
    text-align: center; 
    line-height: 40px; 
    border-radius: 5px; 
    cursor: pointer;
}
</style>
""";

$bus_html .= """ 
<script>
  const busLayout = document.getElementById('busLayout');
  const selectedSeatsDiv = document.getElementById('selectedSeats');
  const totalAmountDiv = document.getElementById('totalAmount');
  const seatInput = document.querySelector('input[name="seats"]');
  const priceInput = document.querySelector('input[name="total-amount"]');
  const clearSeatFieldBtn = document.getElementById('clearSeatField');
  const clearSelectionsBtn = document.getElementById('clearSelections');
  let selectedSeatEntries = [];
  let totalAmount = 0;

  busLayout.addEventListener('click', function(e) {
    if (e.target.classList.contains('seat') && (e.target.style.backgroundColor === 'green' || e.target.style.backgroundColor === 'blue')) {
      const seat = e.target.getAttribute('data-seat');
      const price = parseInt(e.target.getAttribute('data-price'));
      const entry = { seat, price };
      const index = selectedSeatEntries.findIndex(s => s.seat === seat);

      if (index === -1) {
        selectedSeatEntries.push(entry);
        totalAmount += price;
        e.target.style.backgroundColor = 'blue';
      } else {
        selectedSeatEntries.splice(index, 1);
        totalAmount -= price;
        e.target.style.backgroundColor = 'green';
      }
      updateDisplay();
    }
  });

  function updateDisplay() {
    const seatList = selectedSeatEntries.map(entry => `${entry.seat}(${entry.price})`);
    selectedSeatsDiv.textContent = 'Selected Seats: ' + seatList.join(', ');
    totalAmountDiv.textContent = 'Total Amount: ' + totalAmount;
    seatInput.value = selectedSeatEntries.map(entry => entry.seat).join(',');
    priceInput.value = totalAmount;
  }

  clearSeatFieldBtn.addEventListener('click', function() {
    seatInput.value = '';
  });

  clearSelectionsBtn.addEventListener('click', function() {
    selectedSeatEntries.forEach(entry => {
      const seatDiv = document.querySelector(`[data-seat="${entry.seat}"]`);
      if (seatDiv) {
        seatDiv.style.backgroundColor = 'green';
      }
    });
    selectedSeatEntries = [];
    totalAmount = 0;
    updateDisplay();
  });
</script>
""";

return $bus_html;
  • Purpose: To add the necessary styles and JavaScript for seat selection functionality.
  • Steps:
    • Defines CSS styles for the bus layout and seats.
    • Implements JavaScript to handle seat selection, updating the display of selected seats and total amount, and clearing selections.
    • Adds event listeners for seat clicks, updating the selection state, and buttons to clear selections and the seat field.
  • This script dynamically generates an interactive seat layout for the booking table, allowing users to select and deselect seats, view seat prices, and see the total cost in real-time.

Steps in JavaScript Code Explanation

  • Initialization of Variables and Elements
    • The script starts by selecting various elements from the DOM and storing them in variables for easy access later.
    • These include the bus layout container, selected seats display, total amount display, input fields for seats and total amount, and buttons to clear selections and seat input.
  • Event Listener for Seat Selection
    • An event listener is added to the bus layout container (busLayout). This listener waits for click events.
    • When a seat (a div with the class 'seat') is clicked, the script checks if the seat is already selected (indicated by its background color being 'green' or 'blue').
  • Handling Seat Selection
    • If the seat is not already selected, it retrieves the seat number and price from the clicked seat's data attributes.
    • It then creates an entry object for the selected seat and checks if the seat is already in the selectedSeatEntries array.
    • If the seat is not in the array, it adds the entry to the array, updates the total amount, and changes the seat's background color to 'blue'.
    • If the seat is already in the array, it removes the entry, updates the total amount, and changes the seat's background color back to 'green'.
  • Updating the Display
    • The updateDisplay function is called to refresh the display of selected seats and total amount.
    • This function creates a list of selected seats with their prices and updates the selectedSeatsDiv text content.
    • It also updates the totalAmountDiv with the current total amount.
    • The seat and price input fields are updated with the current selections and total amount, respectively.
  • Clearing Seat Input Field
    • An event listener is added to the clear seat input field button (clearSeatFieldBtn).
    • When clicked, it simply clears the value of the seat input field, but does not affect the actual selections.
  • Clearing All Selections
    • Another event listener is added to the clear selections button (clearSelectionsBtn).
    • When clicked, it iterates over all selected seats and resets their background color to 'green'.
    • It then clears the selectedSeatEntries array and resets the total amount to 0.
    • Finally, it calls updateDisplay to refresh the display.

Creating a PDF Passenger Manifest for Trips and Attaching it to the Trips Table

As the final piece of this project, let's focus on building a PDF passenger manifest. Users will be able to effortlessly generate a comprehensive list of all passengers traveling on a specific journey with a single click of a button. We will generate a PDF document and attach it to the trip item.

Go to "Table Settings" of and Add a new file Field. Call it "Files".

  • Navigate to your table's settings.
  • Look for the "Automations" tab and click on it.
  • Click on "New Flow" button.
  • Name your automation flow "Generate Passenger Manifest".
  • Choose "Manually By Automation or Button" as the trigger for this automation.
  • This means that this automation is triggered either by click of a button or through another automation.
  • Leave the conditions section blank as we want the automation to run unconditionally.

Automation Action (THEN Section)

  • Custom Variable Block:
    • Name: table
    • Value:
      return """
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Styled Table for PDF</title>
              <style>
                  body {
                      font-family: Arial, sans-serif;
                  }
                  table {
                      width: 100%;
                      border-collapse: collapse;
                      margin: 20px 0;
                      font-size: 18px;
                  }
                  table, th, td {
                      border: 1px solid #ddd;
                  }
                  th, td {
                      padding: 12px;
                      text-align: left;
                  }
                  th {
                      background-color: #f2f2f2;
                  }
                  tr:nth-child(even) {
                      background-color: #f9f9f9;
                  }
                  tr:hover {
                      background-color: #f1f1f1;
                  }
                  caption {
                      caption-side: top;
                      font-size: 24px;
                      margin-bottom: 10px;
                      font-weight: bold;
                  }
              </style>
          </head>
          <body>
          
          <table>
              <thead>
                  <tr>
                      <th>Name</th>
                      <th>Emails</th>
                      <th>Seats</th>
                  </tr>
              </thead>
              <tbody>
          """;
    • Note: To create the passenger manifest PDF, we'll start by defining a well-structured table. This table will display all the essential details about the passengers traveling on a specific trip. We'll then apply appropriate styles to enhance readability and presentation within the PDF document.
  • Custom Variable Block:
    • Name: row
    • Value: Leave it blank
    • Note: This variable, named row, will act as a container to hold the table rows we dynamically generate as we loop through each passenger booking.
  • For Each Related Block:
    • Current: Trips
    • Loop: Related from: Bookings
    • Field: Any Field
    • Here we'll then iterate through all related booking records associated with this specific trip.
    • During this loop, we'll have access to all the fields (data points) within each individual booking record. This allows us to retrieve the necessary information for the manifest.
    • Custom Variable Block:
      • Name: id
      • Value: Bookings: Customer["id"] / @{2301:customer}["id"]
      • Note: This extracts the customer id from the current booking item and stores it in the id variable.
    • Custom Variable Block:
      • Name: name
      • Value: Bookings: Customer["title"] / @{2301:customer}["title"]
      • Note: This extracts the customer title from the current booking item and stores it in the name variable.
    • Custom Variable Block:
      • Name: email
      • Value: Leave it blank
      • Note: We don't have the customer's email yet. We'll retrieve it in the next step.
    • Search Loop Block:
      • Table: Customers
      • Where: ID
      • Condition: Equal To
      • Value: Variable :id / @{var:id}
      • Custom Variable Block:
        • Name: email
        • Value: Customers: Email / @{2242:email}
        • Note: After finding the customer using their ID, we can now populate the email variable with their email address.
    • Custom Variable Block:
      • Name: row
      • Value:
        return @{var:row} + """
        <tr>
        <td> """ + @{var:name}  + """</td>
        <td> """ +  @{var:email}  + """</td>
        <td> """ + @{2301:seats}  + """</td>
        </tr>
                        """
      • Note: Now that we have all the needed customer details (ID, name, and email), we can create a table row containing this information. This row is then appended to the row variable, which accumulates all passenger rows for the final manifest table.
  • Custom Variable Block:
    • Name: table
    • Value:
      return @{var:table} + @{var:row} + """
                  </tbody>
                  </table>
              """
    • Note: This completes the construction of the passenger manifest table within the table variable. It now holds the entire table structure containing information for all passengers associated with the trip.
  • Custom Variable Block:
    • Name: bus
    • Value: This Record: Bus["title"] / @{current:bus}["title"]
    • Note: This extracts the bus title from the trip item and stores it in the bus variable.
  • Build A PDF Block:
    • Record: This Trips Record
    • Field: Files
    • Filename: Manifest
    • Page Size: Letter
    • Header:
      • Heading: Passenger Manifest
      • Trip:  This Record: Title / @{current:title}
      • Date:  This Record: Date / @{current:date}
      • Destination:  This Record: Destination / @{current:destination}
      • Bus:  Variable: bus / @{var:bus}
    • Content: Variable: table / @{var:table}
  • Save the Automation.
  • All set! Now to make this automation easily accessible, let's add a button. Head over to "Table Settings" and click "Add New Button." Name it something clear like "Generate Passenger Manifest". When someone clicks this button, we want it to run the automation we just built. So, under "Flow," choose the "Passenger Manifest" flow from the list. Save the changes.

Simulation and Testing

To test the automation, head over to a specific trip record and click the "Generate Passenger Manifest" button. This will trigger the automation you just built, and a PDF passenger manifest containing a complete list of passengers for that trip will be automatically attached to the record.

Creating a Subtable for All the Bookings for a Customer

Now that we've completed the bookings table functionality, let's expand the customer table. We'll achieve this by adding a subtable that displays a customer's purchase history or bookings directly on their profile page.

Setting Up the Subtable

  • Navigate to Table Settings
  • Within the table settings, look for a tab labeled "Tab Groups". This section allows you to configure additional subtables within your main "Customers" table. Select the "Tab Groups" Tab
  • At the bottom right of the "Tab Groups" section, you'll likely find a list of available incoming relationships.
  • Click the relationship named "Bookings" from the list. This signifies the connection between the "Customers" table and the "Bookings" table. Selecting this establishes the basis for the subtable.
  • Once you choose the "Bookings" relationship, a subtable will be automatically created. Select the specific fields you want to display in the subtable. In this case, choose Trip, Seats and Total Amount.
  • Once you've selected the desired fields, confirm and save your configuration. Now, when you open a customer record, you'll see a new tab with a subtable displaying all the bookings associated with that customer.

You've Built a Travel Booking Powerhouse with InfoLobby!

Congratulations! You've reached the end of this comprehensive guide on building a powerful travel booking application with InfoLobby. Throughout this series, we've explored the extensive capabilities of InfoLobby, transforming it into a tailored solution for Kingdom Tours and Travels. From managing customers and buses to scheduling trips, facilitating bookings, and generating passenger manifests, InfoLobby has proven itself as a versatile and feature-rich platform.

Remember, this is just a glimpse into the vast potential of InfoLobby. Its intuitive interface and robust functionalities can be adapted to a wide range of business needs. With InfoLobby by your side, you have the power to craft innovative solutions and streamline your operations like never before.

InfoLobby © 2024 Globi Web Solutions