Skip to content

Cruise Module - Frontend Implementation Specification

Overview

This document provides detailed technical documentation for the Sailing Management frontend implementation, covering three dedicated pages. The module manages cruise companies, itineraries, sailings, pricing, pre-bookings, and agent bookings.

Pages:

Page HTML JS Module Lines (HTML) Lines (JS)
Dashboard tqweb-adm/cruise-dash.html cruise-dash.js ~420 ~350
Itinerary Management tqweb-adm/cruise-itin.html cruise-itin.js ~760 ~1500
Agent Booking tqweb-adm/cruise-agt-book.html cruise-agt-book.js ~310 ~870

Shared Infrastructure:

File Purpose Lines
js/modules/cruise-common.js CruiseAPI client, initCommon(), dimension helpers, statusBadge() ~200
js/modules/exchange-rate-service.js Currency config, rate fetching, dropdown population ~100
css/cruise.css All cruise-specific styles (CSS variables, components, print) ~600

Technology Stack

Core Libraries

Library Version Purpose
jQuery 3.7.1 DOM manipulation, event handling
Bootstrap 5.x CSS framework, grid, modals, badges
Notify.js Custom Toast notifications
ES6 Modules Native Module import/export

JavaScript Features Used

  • ES6 Modules: import/export syntax
  • Arrow Functions: Concise callbacks
  • Template Literals: String interpolation for HTML rendering
  • async/await: Not used; Promises via tlinq() with .then() callbacks
  • Spread/Rest: Array manipulation for data processing

Authentication Pattern

All pages use the same auth pattern:

<body style="visibility:hidden">
await $site.setGlobalHandlers({ requireAuth: true });
document.body.style.visibility = 'visible';

Module Import Pattern

<script type="module">
    import * as $mod from './js/modules/cruise-dash.js';
    window.CruiseDash = $mod;
    // initialization in DOMContentLoaded
</script>

Shared Infrastructure

cruise-common.js

Provides the CruiseAPI object with methods for all cruise endpoints. All methods return Promises that resolve to the API data directly (via tlinq()).

Key Exports:

Export Type Purpose
CruiseAPI Object All API endpoint methods
initCommon() Function Loads dimension data (areas, ports, cabin types, charge types, ships)
getAreas(), getPorts(), etc. Functions Cached dimension data getters
findArea(), findPort(), etc. Functions Lookup by ID helpers
statusBadge(status) Function Returns Bootstrap badge HTML for a status value

CruiseAPI Methods (grouped):

Group Methods
Company listCompanies(), readCompany(), writeCompany(), deleteCompany()
Dimension listAreas(), listPorts(), listCabinTypes(), listChargeTypes(), listShips(), + write/delete for each
Ship Cabin listShipCabins(), writeShipCabin(), deleteShipCabin(), bulkAssignCabins()
Itinerary listItineraries(), readItinerary(), writeItinerary(), deleteItinerary(), changeItineraryStatus(), getItineraryTree()
Template listTemplates(), writeTemplate(), deleteTemplate()
Pricing Templates listCabinPriceTemplates(), writeCabinPriceTemplate(), deleteCabinPriceTemplate(), initCabinPriceTemplates()
Charge Templates listOtherChargeTemplates(), writeOtherChargeTemplate(), deleteOtherChargeTemplate()
Cruise listCruises(), readCruise(), writeCruise(), deleteCruise(), changeCruiseStatus()
Cruise Point listCruisePoints(), writeCruisePoint(), deleteCruisePoint(), regeneratePoints()
Cabin Charge listCabinCharges(), writeCabinCharge(), deleteCabinCharge(), initCabinCharges()
Other Charge listOtherCharges(), writeOtherCharge(), deleteOtherCharge()
Pre-Booking listPrebookings(), writePrebooking(), deletePrebooking(), managePrebookings()
Booking listBookings(), readBooking(), writeBooking(), deleteBooking(), changeBookingStatus()
Passenger listPassengers(), writePassenger(), deletePassenger()
Booked Cabin listBookedCabins(), writeBookedCabin(), deleteBookedCabin()
Add-on listAddons(), writeAddon(), deleteAddon()

exchange-rate-service.js

Provides exchange rate auto-fill functionality for pricing dialogs.

Key Exports:

Export Purpose
loadCurrencyConfig() Loads local currency and available currencies from config
fetchRates() Fetches current exchange rates with 24h cache
populateCurrencySelect(selectId) Populates a <select> element with currency options

Auto-fill behavior: When a currency dropdown changes, the exchange rate field is automatically populated. If the selected currency equals the local currency, rate is set to 1.0. Otherwise, the cached rate is used. The exchange rate field remains editable for manual override.

cruise.css

CSS variables and component styles shared across all three pages.

Key CSS Sections:

Section Components
CSS Variables --cruise-primary, company colors
Dashboard .company-accordion, .kpi-card, .itin-card, .filter-bar
Itinerary .itin-section, .sailing-bar, .sailing-pill, .indicator-badge, .itin-table, .prebook-info
Booking .sailing-strip-container, .sailing-strip, .sailing-pill, .visa-badge, .age-badge, .own-badge, .nationality-autocomplete, .booking-pane-left, .booking-pane-right
Tables .itin-table, .bookings-table
Print @media print rules for sailing strip, badges, pane scroll override

Page 1: Dashboard (cruise-dash.html + cruise-dash.js)

Layout

┌─────────────────────────────────────────────────────────────┐
│  Breadcrumb: Dashboard > Sailings                           │
├──────────────────────┬──────────────────────────────────────┤
│                      │  KPI Cards (5)                        │
│  Company Accordion   │  [Areas] [Ships] [Itins] [Sails] [Bk]│
│                      ├──────────────────────────────────────┤
│  [Company 1]  ▼      │  Filter Bar                          │
│    Ship A            │  [Search] [Area ▼] [Status ▼]       │
│    Ship B            ├──────────────────────────────────────┤
│    [+ Ship] [Edit]   │  Itinerary Cards Grid                │
│                      │  ┌─────────┐ ┌─────────┐ ┌─────────┐│
│  [Company 2]  ►      │  │ Itin 1  │ │ Itin 2  │ │ Itin 3  ││
│                      │  │ 3 Ports │ │ 5 Ports │ │ 4 Ports ││
│  [+ Company]         │  │Edit Copy│ │Edit Copy│ │Edit Copy││
│                      │  │Mng Print│ │Mng Print│ │Mng Print││
│  [Settings]          │                                      │
└──────────────────────┴──────────────────────────────────────┘

Left Pane (col-md-4): Company accordion with collapsible sections. Each company header uses the company's color field. Contains ship list, add/edit company buttons, add ship button. Settings button opens dimension data management.

Right Pane (col-md-8): KPI summary cards (5 cards: Sailing Areas, Ships, Itineraries, Sailings, Bookings), filter bar (search input, area dropdown, status dropdown, New Itinerary button), and itinerary card grid. Each card shows itinerary name, ship, duration, port count, area, status badge, sailing count, and a footer with Edit, Copy, Manage, and Print buttons. Creating a new itinerary redirects to cruise-itin.html?itineraryId=N. Copying an itinerary opens the itinerary dialog pre-populated with source data (name suffixed with "(Copy)") and redirects after save. Print opens the itinerary page in a new tab with ?print=true to auto-trigger window.print().

JS Module State

let companies = [];
let ships = [];
let itineraries = [];
let areas = [];
let selectedCompany = null;
let portCounts = {};           // template count per itinerary (for port display)
let totalBookingCount = 0;     // total bookings across all sailings (KPI)

Key Functions

Function Purpose
initializePage() Load dimension data, companies, ships, itineraries; render UI
renderCompanyAccordion() Build accordion HTML from companies + ships
renderKPIs() Display 5 summary count cards (areas, ships, itineraries, sailings, bookings)
renderItineraryCards() Filter and display itinerary grid with port counts
applyFilters() Apply search/area/status filters to itinerary cards
saveItinerary() Save new/edited itinerary; redirects to cruise-itin.html for new itineraries; handles deep-copy for copy operations
copyItinerary(id) Open itinerary dialog pre-populated with source data + "(Copy)" suffix

Page 2: Itinerary Management (cruise-itin.html + cruise-itin.js)

Layout

┌─────────────────────────────────────────────────────────────┐
│  Breadcrumb: Dashboard > Sailings > [Itinerary Name]        │
├────────────────────────────┬────────────────────────────────┤
│  LEFT PANE (col-md-6)      │  RIGHT PANE (col-md-6)        │
│                            │                                │
│  ┌── Itinerary Info ─────┐ │  ┌── Sailing Bar ────────────┐│
│  │ Code, Name, Duration  │ │  │ [15 Mar] [22 Mar] [+ New] ││
│  │ Area ▼, Ship ▼        │ │  └──────────────────────────┘│
│  │ [Save] [Status] [Del] │ │                                │
│  └───────────────────────┘ │  ┌── Sailing Details ────────┐│
│                            │  │ [Regen] [Status ▼] [Del]  ││
│  ┌── Template ───────────┐ │  │                            ││
│  │ # Port  DayA DayD  ▲  │ │  │ Cruise Points Table       ││
│  │ 1 Dubai  0    0     │  │ │  │ # Port Arrive Depart Acts ││
│  │ 2 Doha   1    1     │  │ │  │                            ││
│  │ [+ Add]              │  │ │  │ Cabin Charges Table       ││
│  └───────────────────────┘ │  │  │ Cabin Amt Ccy Prebk Avail││
│                            │  │ [Prebook]                  ││
│  ┌── Pricing Templates ─┐ │  │                            ││
│  │ Cabin Prices (Default)│ │  │ Other Charges Table        ││
│  │ [Init Cabins]         │ │  │ Type Ind Amt Ccy Mand Acts ││
│  │                       │ │  │                            ││
│  │ Other Charges (Default│ │  │ Bookings Table             ││
│  │ [+ Add]               │ │  │ Customer Cabin #Pax Status ││
│  └───────────────────────┘ │  └────────────────────────────┘│
└────────────────────────────┴────────────────────────────────┘

URL Parameters

  • itineraryId (required): ID of the itinerary to manage
  • print (optional): If "true", auto-triggers window.print() after 500ms delay (used by dashboard Print button)

JS Module State

let itinerary = null;
let templates = [];
let cabinPriceTemplates = [];
let otherChargeTemplates = [];
let cruises = [];            // sailings for this itinerary
let selectedCruise = null;
let cruisePoints = [];
let cabinCharges = [];
let otherCharges = [];
let prebookings = [];
let bookings = [];
let shipCabins = [];
let currencyConfig = null;

Key Functions

Function Purpose
initializePage() Parse URL params, load itinerary + all related data
loadItineraryData() Load itinerary, templates, pricing templates
renderItineraryInfo() Populate info form fields
renderTemplateTable() Build template port sequence table
renderCabinPriceTemplates() Build cabin pricing template table with Init Cabins
renderOtherChargeTemplates() Build charge template table with indicator badges
renderSailingBar() Build sailing pill buttons
selectSailing(cruiseId) Load all sailing details
renderCruisePoints() Build cruise points table
renderCabinCharges() Build cabin charges with Prebk/Avail columns
renderOtherCharges() Build other charges with indicator badges
renderBookings() Build bookings table with clickable rows
showPrebookDialog() Open prebook management dialog
handleCurrencyChange() Auto-fill exchange rate from cached rates
Dialog ID Purpose Key Fields
template_dlg Add/edit template entry Port, StopSeq, DayOffsetArr, DayOffsetDep, ArriveTime, DepartTime
cabintemplate_dlg Edit cabin price template Amount, Currency (auto-fill exrate), ExRate, Available
chargetemplate_dlg Add/edit charge template ChargeType, Amount, Currency (auto-fill), ExRate, Mandatory, PaxType, Indicator
sailing_dlg Create new sailing StartDate
cruisepoint_dlg Edit cruise point Port, ArrivalDT, DepartureDT
cabincharge_dlg Edit cabin charge Amount, Currency (auto-fill), ExRate, Available
othercharge_dlg Add/edit other charge ChargeType, Amount, Currency (auto-fill), ExRate, Mandatory, PaxType, Indicator
prebook_dlg Manage pre-bookings CabinType selector, TargetCount, Cost, Price, Commission, ApplyToAll
confirm_dlg Generic confirmation Message, Confirm button

New Features

  1. Indicator Badge: Single-letter badge on other charge templates and charges, rendered as .indicator-badge span
  2. Exchange Rate Auto-Fill: On currency dropdown change → fetch rate → populate exrate field (editable)
  3. Pre-Booking Columns: Cabin charges table shows Prebk (count) and Avail (prebk - booked/reserved)
  4. Bookings Table: Shows current bookings, rows clickable → navigates to cruise-agt-book.html?bookingId=N
  5. Print Auto-Trigger: When ?print=true URL parameter is present, window.print() is called after page load (500ms delay). Existing @media print CSS hides interactive elements.

Page 3: Agent Booking (cruise-agt-book.html + cruise-agt-book.js)

Layout

┌─────────────────────────────────────────────────────────────┐
│  Breadcrumb: Dashboard > Sailings > [Itinerary] > Booking   │
├─────────────────────────────────────────────────────────────┤
│  Sailing Strip (full width, scrollable)                     │
│  ◄ [15 Mar MED7] [22 Mar MED7] [29 Mar MED7] [5 Apr] ... ►│
├────────────────────────────┬────────────────────────────────┤
│  LEFT PANE (col-md-6)      │  RIGHT PANE (col-md-6)        │
│  (independent scroll)      │  (independent scroll)          │
│                            │                                │
│  ┌── Cabin Availability ─┐ │  ┌── Booking Header ─────────┐│
│  │ Cabin  Max  Chrg Prebk│ │  │ PT-2302-AB12CD34  [+ New] ││
│  │ Suite  4   $500  2[+-]│ │  │ [Status ▼]  [Delete]      ││
│  │ Ocean  2   $300  ---  │ │  └──────────────────────────┘│
│  └───────────────────────┘ │                                │
│                            │  ┌── Passengers (3 of 5) ────┐│
│  ┌── Bookings ───────────┐ │  │ Name  Nat  Age Visa  Acts ││
│  │ ID  Cust  Pax Amt Stat│ │  │ [+ Add Passenger]         ││
│  │ PT.. Smith 2  800 Doc │ │  │ [capacity warning if >max]││
│  │ PT.. Jones 4  950 Pay │ │  └──────────────────────────┘│
│  │ [+ New Booking]       │ │                                │
│  └───────────────────────┘ │  ┌── Booked Cabins ─────────┐│
│                            │  │ Cabin Pax Own Price  Acts  ││
│                            │  │ Total:        $800         ││
│                            │  │ [+ Add Cabin]              ││
│                            │  └──────────────────────────┘│
│                            │                                │
│                            │  ┌── Add-ons ────────────────┐│
│                            │  │ Type   Pax    Amt    Acts  ││
│                            │  │ Total:        $150         ││
│                            │  │ [+ Add Add-on]             ││
│                            │  └──────────────────────────┘│
└────────────────────────────┴────────────────────────────────┘

URL Parameters

  • cruiseId (optional): Pre-select a sailing
  • bookingId (optional): Pre-select a booking (loads its sailing automatically)

JS Module State

let allSailingData = [];      // all sailings across itineraries for the company
let selectedCruise = null;
let selectedItinerary = null;
let selectedCompany = null;
let shipCabins = [];
let cabinCharges = [];
let otherCharges = [];
let prebookings = [];
let cruisePoints = [];
let bookings = [];
let selectedBooking = null;
let passengers = [];
let bookedCabins = [];
let addons = [];
let countryList = [];
let currencyConfig = null;

Key Functions

Function Purpose
initializePage() Parse URL params, load all sailings, render UI
loadAllSailings() Load all cruises with itinerary data for the sailing strip
renderSailingStrip() Build scrollable pill buttons with company-colored borders
selectSailing(cruiseId) Load cabin availability, prebookings, bookings for a sailing
renderCabinAvailability() Build cabin table with charge, prebk count, +/- buttons; shows --- when no prebooks
renderBookingsList() Build bookings table with ID, Customer name, Pax, Amount, Status columns
selectBooking(bookingId) Load and display booking details (passengers, cabins, addons)
createNewBooking() Open new booking dialog, handle creation flow
generateBookingNumber() Generate PT-DDMM-XXXXXXXX using createId('', 8)
renderPassengers() Build passengers table with visa/age badges; updates "X of Y" count header; checks pax capacity warning
renderBookedCabins() Build cabins table with own badges and totals; checks pax capacity warning
renderAddons() Build add-ons table with totals
checkPaxCapacityWarning() Show/hide red warning if total passengers > sum of cabin maxPax capacity
populateAddonPassengerSelect(paxType) Filter passenger dropdown for add-on by paxType (adult-only excludes children)
findAvailablePrebook(cabinTypeId) Check for Available prebookings to assign as own
checkVisaForPassenger(passengerId) Call visa/lookuprequirement for each destination port
setupNationalityAutocomplete(inputId) Attach country autocomplete to nationality input
computeAgeCategory(dob, sailingDate) Return Adult/Child/Infant based on age
updateBookingTotals() Recalculate and save totalAmount from cabins + addons
Dialog ID Purpose Key Fields
newbooking_dlg Create new booking Adults, Children, Infants, CabinType, Lead FirstName, LastName, Nationality (autocomplete)
passenger_dlg Add/edit passenger FirstName, LastName, Nationality (autocomplete), DOB, PassportNum, PassportExpiry, Remarks; Visa info area
addcabin_dlg Add cabin to booking CabinType selector, NumPax (dynamic max from cabin maxPax), Price; Prebook info display
addaddon_dlg Add add-on to booking ChargeType selector (with data-paxtype), Passenger selector (filtered by paxType), Amount, Currency
prebook_dlg Quick prebook management CabinType, TargetCount, Cost, Price, Commission
confirm_dlg Generic confirmation Message, Confirm button

New Booking Flow

  1. Agent clicks "New Booking" → newbooking_dlg opens
  2. Agent enters pax counts (auto-calculates total), selects cabin type, enters lead passenger with nationality autocomplete
  3. On Save:
  4. generateBookingNumber() creates PT-DDMM-XXXXXXXX
  5. CruiseAPI.writeBooking() creates booking with status "Documents pending"
  6. CruiseAPI.writePassenger() creates lead passenger (isLeadPassenger=true)
  7. findAvailablePrebook() checks for Available prebookings → assigns cabin as own/non-own
  8. CruiseAPI.writeBookedCabin() creates cabin assignment
  9. updateBookingTotals() recalculates and saves total
  10. UI navigates to booking detail view

Cabin Own/Prebook Logic

function findAvailablePrebook(shipCabinId) {
    // Filter Available prebookings for this cabin type
    const available = prebookings.filter(p =>
        p.shipCabinId === shipCabinId && p.status === 'Available');
    // Count already-booked own cabins for this type
    const ownCount = bookedCabins.filter(c =>
        c.shipCabinId === shipCabinId && c.isOwn).length;
    // If available prebooks > own count, assign as own
    if (available.length > ownCount) {
        return available[ownCount]; // return the prebook to use
    }
    return null; // no available prebook
}

Visa Check Logic

async function checkVisaForPassenger(passenger) {
    // Get unique destination countries from cruise points
    const portCountries = [...new Set(cruisePoints.map(p => p.portCountry))];
    // Map country names to alpha2 codes via countryList
    // For each destination: call visa/lookuprequirement
    // Display visa-required / visa-free / visa-on-arrival badges
}

Age Category Computation

function computeAgeCategory(dob, sailingDate) {
    const age = /* years between dob and sailingDate */;
    if (age >= 12) return 'Adult';
    if (age >= 2) return 'Child';
    return 'Infant';
}

Cabin Capacity Validation (maxPax)

When adding a cabin to a booking, the system enforces the cabin type's maximum passenger capacity:

  1. Dynamic max attribute: On cabin type change in the add-cabin dialog, $('#addcabin_pax').attr('max', maxPax) is set dynamically
  2. Client-side guard: In addCabin(), if numPax > maxPax, an error is shown and the operation is blocked
  3. Backend validation: CruiseFacade.validateCabinPaxCapacity() checks numPax <= maxPax and throws CRU0207 if exceeded
  4. Capacity warning: checkPaxCapacityWarning() compares total passenger count against the sum of maxPax across all booked cabins. If passengers exceed capacity, a red alert is shown below the passengers section

Adult-Only Add-On Filtering

Charge types have a paxType field ("adult", "child", "all", "per_cabin"). When adding an add-on:

  1. Charge type options include data-paxtype attribute
  2. On charge type change, populateAddonPassengerSelect(paxType) re-filters the passenger dropdown:
  3. "adult" → excludes passengers with age < 12
  4. "child" → excludes passengers with age >= 12
  5. "per_cabin" → disables passenger dropdown (set to null)
  6. "all" → shows all passengers

Independent Pane Scrolling

The left and right panes on the agent booking page scroll independently via CSS: - .booking-pane-left, .booking-pane-right { max-height: calc(100vh - 160px); overflow-y: auto; } - Print override removes scroll constraints: @media print { max-height: none !important; overflow: visible !important; }

Passenger Count Header

The passengers section header displays an "X of Y" count (e.g., "3 of 5") showing current passengers vs. total expected (sum of adults + children + infants from the booking). Updated in renderPassengersTable().

New Booking Button in Right Pane

A "New" button (btn-success, bi-plus-lg) is placed in the right pane booking header, wired to openNewBookingDialog. This provides quick access to create a new booking without scrolling to the left pane.


Files Summary

File Path Lines Purpose
Dashboard HTML tqweb-adm/cruise-dash.html ~420 Dashboard page structure
Dashboard JS tqweb-adm/js/modules/cruise-dash.js ~350 Dashboard logic
Itinerary HTML tqweb-adm/cruise-itin.html ~760 Itinerary page with 9 modal dialogs
Itinerary JS tqweb-adm/js/modules/cruise-itin.js ~1500 Itinerary management logic
Booking HTML tqweb-adm/cruise-agt-book.html ~310 Agent booking page with 6 dialogs
Booking JS tqweb-adm/js/modules/cruise-agt-book.js ~870 Booking management logic
Common API tqweb-adm/js/modules/cruise-common.js ~200 Shared CruiseAPI client
Exchange Rates tqweb-adm/js/modules/exchange-rate-service.js ~100 Currency rate service
CSS tqweb-adm/css/cruise.css ~600 All cruise styles

Key Dependencies

Module Used For
cruise-common.js CruiseAPI, initCommon(), dimension getters/finders, statusBadge()
exchange-rate-service.js loadCurrencyConfig(), fetchRates(), populateCurrencySelect()
globals.js escapeHtml, formatDisplayDate, formatDisplayDateTime, formatDateTimeLocal, createId, tlinq, getUserSession
pageutil.js modalShow(), modalHide(), loadBootstrapTemplates()
site.js setGlobalHandlers() for auth
visamgmt.js (reference) Country loading pattern, visa lookup pattern