{{ currentPage }}
{{ activeHeroLabel }}
{{ displayActiveTripsCount }}
{{ activeHeroSubtext }}
View bookings →
Total Trips
{{ nonCancelledTrips.length }}
Total Bookings
{{ stats.totalBookings ?? nonCancelledBookings }}
Total Trips YTD
{{ totalTravelPlansYTD }}
Total Managed Value
{{ maskVal('$' + totalRevenue) }}
Payment Pending
{{ paymentPendingTrips }}
Future Trips
{{ futureTrips }}
Scheduled Reminders
{{ remindersSummary.total }}
{{ reminderBadge.count }} {{ reminderBadge.label }}Commissions YTD
{{ maskVal('$' + ytdCommission) }}
Managed Value YTD
{{ maskVal('$' + managedValueYTD) }}
Total Commissions
{{ maskVal('$' + totalCommissions) }}
Total Collected Commissions
{{ maskVal('$' + totalCollectedCommissions) }}
Recently Added Trips
No recent trips yet
Added trips will appear here.
{{ trip.destination }}
Client: {{ maskName(trip.clientName) }}
Added {{ formatTripAddedDate(trip) }}
{{ amountsVisible ? formatMoney(trip.cost) : '••••••' }}
{{ trip.status }}TAI
AI Concierge Assistant
Destinations Map
{{ (worldMapPins || []).length }} locations · click a pin to filter trips
No destinations mapped yet
Add bookings to see them appear on the map
Top Routes
Any booking
Active trip
Pin size ∝ bookings
Reservation Performance
Monthly activity for the last 12 months
No reservation data in the last 12 months
| Status{{ tripsSort.key==='status' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Client Name {{ tripsSort.key==='clientName' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Client Phone | Client E-mail {{ tripsSort.key === 'clientEmail' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Trip ID {{ tripsSort.key === 'tripId' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Start / Departure Date{{ tripsSort.key==='startDate' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | End / Return Date {{ tripsSort.key === 'endDate' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Destination{{ tripsSort.key==='destination' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Total Value ($){{ tripsSort.key==='cost' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Paid Amount ($) {{ tripsSort.key === 'payedAmount' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Payment Due Date {{ tripsSort.key === 'paymentDueDate' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Notes | Total Commission ($) {{ tripsSort.key === 'totalCommission' ? (tripsSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Trip Added{{ tripsSort.key==='tripAdded' ? (tripsSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Actions | |||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ trip.status }} |
{{ maskName(trip.clientName) }}
Doc: #{{ trip.id }}
{{ getBookingCount(trip) }} booking{{ getBookingCount(trip) === 1 ? '' : 's' }}
|
{{ trip.clientPhone || '—' }} | {{ amountsVisible ? (trip.clientEmail || '—') : '•••••@•••••' }} | {{ trip.tripId || '—' }} | {{ trip.startDate || '—' }} | {{ trip.endDate || '—' }} |
{{ trip.destination }}
|
{{ amountsVisible ? formatMoney(trip.cost) : '••••••' }} | {{ trip.payedAmount != null && trip.payedAmount !== '' ? (amountsVisible ? formatMoney(trip.payedAmount) : '••••••') : '—' }} | {{ trip.paymentDueDate || '—' }} | {{ trip.notes || '—' }} | {{ trip.totalCommission != null && trip.totalCommission !== '' ? (amountsVisible ? formatMoney(trip.totalCommission) : '••••••') : '—' }} | {{ trip.tripAdded || '—' }} |
|
|||||||||||||||||||||||||||||||||||||||
|
Bookings
{{ getBookingCount(trip) }} total
{{ trip.tripId }}
No bookings yet. Click "Add Booking" to add one.
No Trip ID on this record — run the backfill first (see README).
Loading bookings…
Notes
{{ trip.notes || '—' }} |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
No matching trips found
Try adjusting your filters, search terms, or status tabs, or import a trips file.
Scheduled
{{ remindersSummary.total }}
Total active reminders
Due Today
{{ remindersSummary.dueToday }}
Ready to send today
This Week
{{ remindersSummary.thisWeek }}
Upcoming in the next 7 days
Reminder Queue
Keep track of follow-ups and customer touchpoints.
| Trip ID | Client Name | Destination | Client E-mail | Reminder Date | Reminder Time | Reason | Action |
|---|---|---|---|---|---|---|---|
| {{ reminder.tripId || reminder.trip?.tripId || reminder.trip?.id || '—' }} | {{ reminder.tripDisplay || reminder.clientName || 'Client' }} | {{ reminder.destination || '—' }} | {{ reminder.email || '—' }} | {{ reminder.reminderDate || '—' }} | {{ reminder.reminderTime || '—' }} | {{ reminder.reason || 'No message specified' }} | No trip |
No reminders scheduled
Use a trip reminder from the trips table to add one.
Trip Planner Intake
Capture client wishes, extract structured details, and pre-fill a new itinerary.
{{ plannerFeedback }}
Cloud Storage Archive (Optional)
File will be saved inside the raw_data_trip bucket under the folder name you provide.
Upload Your Trip File
Accepts .csv, .xlsx, or .xls. For Excel files you can select which sheet to import.
Drop it here!
Drag & drop or click to browse
{{ importFile.name }}
{{ importFile ? (importFile.size / 1024).toFixed(1) + ' KB' : 'CSV or Excel (.xlsx / .xls)' }}
Please select a sheet to continue.
Map Columns
{{ importTotalRows }} rows detected. Match your file's columns to app fields. Columns are auto-suggested — adjust as needed.
| App Field | App Field Description | Required | Your File's Column |
|---|---|---|---|
| {{ field.label }} | {{ field.description }} | Required Optional |
File Preview — first {{ importSample.length }} rows
| {{ col }} |
|---|
| {{ row[col] }} |
Blank rows were removed before import
{{ importCleanedRows }} empty row(s) were ignored so the preview and import use only real records.
Map all required fields before importing
Still set to -- Skip --: {{ importMissingRequired.join(', ') }}
{{ importLoadingLabel }}
{{ importTotalRows > 0 ? `${importProcessedRows.toLocaleString()} / ${importTotalRows.toLocaleString()} rows processed` : 'Securely processing booking data' }}
Import Complete!
{{ importResults.imported }}
Imported
{{ importResults.skipped }}
Excluded
{{ importResults.errors.length }}
Errors
Import status
Import succeeded, but some uploaded rows were not kept. Rows that were duplicates or matched an already-normalized record were excluded to avoid double counting.
Archived to: {{ importResults.gcs_url }}
Cleaned-up rows
{{ importResults.cleaned_rows }} empty row(s) were removed before this import was processed.
GCS Archive Failed
{{ importResults.gcs_error }}
The data was still imported to Firestore. Ensure your service account has Storage Admin permission to create buckets.
Skipped Rows
{{ reason }}
Row Errors
{{ err }}
Commission Report USD
Use the period filter below to scope cards, charts, and the detail table.
Trips in Period
{{ commFilteredTrips.length }}
Managed Value
{{ amountsVisible ? '$' + commGrossRevenue : '••••••' }}
Commission Earned
{{ amountsVisible ? '$' + commEarned : '••••••' }}
Avg. Trip Value
{{ amountsVisible ? '$' + commAvgTripValue : '••••••' }}
Top Earning Trip
{{ maskName(commTopEarner.clientName) }}
{{ commTopEarner.destination }}
Trip value: {{ amountsVisible ? '$' + commTopEarner.cost : '••••••' }}
{{ commTopEarner.departurePeriod }}{{ amountsVisible ? '$' + commTopEarner.commission : '••••••' }}
commission
No trips in this period.
vs. Prior Period
Prior {{ commVsPrior.priorLabel }}
{{ amountsVisible ? '$' + commVsPrior.priorFormatted : '••••••' }}
vs {{ commVsPrior.priorLabel }}
{{ commVsPrior.isUp ? '+' : '' }}{{ amountsVisible ? '$' + Math.abs(commVsPrior.diff).toLocaleString() : '••••••' }}
Select a year to compare
against the prior period.
Monthly Managed Value
Total booking value by month
{{ commFilterMetric }}
{{ commFilterMetric === 'Destination' ? 'Top bookings by destination' : 'By month' }}
Booking-Level Detail
{{ sortedCommTrips.length }} booking{{ sortedCommTrips.length !== 1 ? 's' : '' }} shown
| Collection Status {{ commSort.key === 'paymentStatus' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Client Name {{ commSort.key==='clientName' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Reservation Nr {{ commSort.key === 'reservationNr' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Reservation Date {{ commSort.key === 'reservationDate' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Destination {{ commSort.key==='destination' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Departure {{ commSort.key==='startDate' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Value ($) {{ commSort.key==='cost' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Est. Commission ($) {{ commSort.key==='totalCommission' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | # Tracking Form {{ commSort.key==='trackingFormNr' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Submission Deadline {{ commSort.key === 'submissionDeadline' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Est. Payout Date {{ commSort.key === 'payoutDate' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Form Submission Date {{ commSort.key === 'formSubmissionDate' ? (commSort.dir === 'asc' ? '↑' : '↓') : '↕' }} | Collected Commission ($) {{ commSort.key==='collectedCommission' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} | Collection Date {{ commSort.key==='collectionDate' ? (commSort.dir==='asc' ? '↑' : '↓') : '↕' }} |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ resolveCommissionPaymentStatus(row.record, row.trip) }} |
{{ maskName(row.trip.clientName) }}
Doc: {{ row.record?.id ? '#' + row.record.id : 'N/A' }}
|
{{ row.trip.reservationNr || '—' }} | {{ row.booking?.reservationDate || row.trip?.reservationDate || row.trip?.startDate || '—' }} | {{ row.trip.destination }} | {{ row.trip.startDate }} | {{ amountsVisible ? formatMoney(row.displayValue ?? 0) : '••••••' }} | {{ amountsVisible ? formatMoney(row.displayCommission ?? 0) : '••••••' }} | {{ row.record?.trackingFormNr || '—' }} |
{{ commSubmissionDeadline(row.trip) }}
⚠ 60% rate
—
|
{{ commPayoutDate(row.trip) || '—' }} | {{ commFormSubmissionDate(row.trip) || '—' }} | {{ row.record?.commissionCollected != null && row.record?.commissionCollected !== '' ? (amountsVisible ? formatMoney(row.record.commissionCollected) : '••••••') : '—' }} | {{ row.record?.collectionDate || '—' }} |
|
No trips found for this period Try a different month or year. |
|||||||||||||
| Period Total | {{ amountsVisible ? '$' + commGrossRevenue : '••••••' }} | {{ amountsVisible ? '$' + commEarned : '••••••' }} | |||||||||||
Quotes {{ filteredQuotes.length }}
Pending quotes — convert to a trip once confirmed.
| Client Name | Client Phone | Client E-mail | Destination | Departure | Return | Adults | Children | Rooms | Budget | Status | Added | Notes | Actions |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ maskName(quote.clientName) }} | {{ quote.clientPhone || '—' }} | {{ quote.clientEmail ? (amountsVisible ? quote.clientEmail : '••••@•••••') : '—' }} | {{ quote.destination }} | {{ quote.startDate || '—' }} | {{ quote.endDate || '—' }} | {{ quote.adults || 1 }} | {{ quote.children || 0 }} | {{ quote.rooms || 1 }} | {{ amountsVisible ? formatMoney(quote.cost) : '••••••' }} | {{ quote.status }} | {{ quote.tripAdded || '—' }} | {{ quote.notes || '—' }} |
|
|
No quotes yet Click "Add Quote" to create your first quote. |
|||||||||||||