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/exportsyntax - 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:
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 |
@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 manageprint(optional): If"true", auto-triggerswindow.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 |
Modal Dialogs (9)¶
| 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¶
- Indicator Badge: Single-letter badge on other charge templates and charges, rendered as
.indicator-badgespan - Exchange Rate Auto-Fill: On currency dropdown change → fetch rate → populate exrate field (editable)
- Pre-Booking Columns: Cabin charges table shows Prebk (count) and Avail (prebk - booked/reserved)
- Bookings Table: Shows current bookings, rows clickable → navigates to
cruise-agt-book.html?bookingId=N - Print Auto-Trigger: When
?print=trueURL parameter is present,window.print()is called after page load (500ms delay). Existing@media printCSS 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 sailingbookingId(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 |
Modal Dialogs (6)¶
| 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¶
- Agent clicks "New Booking" →
newbooking_dlgopens - Agent enters pax counts (auto-calculates total), selects cabin type, enters lead passenger with nationality autocomplete
- On Save:
generateBookingNumber()creates PT-DDMM-XXXXXXXXCruiseAPI.writeBooking()creates booking with status "Documents pending"CruiseAPI.writePassenger()creates lead passenger (isLeadPassenger=true)findAvailablePrebook()checks for Available prebookings → assigns cabin as own/non-ownCruiseAPI.writeBookedCabin()creates cabin assignmentupdateBookingTotals()recalculates and saves total- 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:
- Dynamic max attribute: On cabin type change in the add-cabin dialog,
$('#addcabin_pax').attr('max', maxPax)is set dynamically - Client-side guard: In
addCabin(), ifnumPax > maxPax, an error is shown and the operation is blocked - Backend validation:
CruiseFacade.validateCabinPaxCapacity()checksnumPax <= maxPaxand throwsCRU0207if exceeded - Capacity warning:
checkPaxCapacityWarning()compares total passenger count against the sum ofmaxPaxacross 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:
- Charge type options include
data-paxtypeattribute - On charge type change,
populateAddonPassengerSelect(paxType)re-filters the passenger dropdown: "adult"→ excludes passengers with age < 12"child"→ excludes passengers with age >= 12"per_cabin"→ disables passenger dropdown (set to null)"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 |