Loading your dashboard ...

Verifying your account and fetching data ...

This may take a few seconds on first load.

TravelAgentOS

Manage your clients' unique escapes

{{ loginError }}

Access restricted to authorized accounts only.

Signing you out ...

See you next time!

Make It Happen

{{ currentPage }}

Ops {{ reminderSystemStatuses.ops.label }} Client {{ reminderSystemStatuses.client.label }}

{{ activeHeroLabel }}

{{ displayActiveTripsCount }}

{{ activeHeroSubtext }}

View bookings →

Total Trips

{{ nonCancelledTrips.length }}

All non-cancelled trips in your portfolio.

Total Bookings

{{ stats.totalBookings ?? nonCancelledBookings }}

Non-cancelled booking records only.

Total Trips YTD

{{ totalTravelPlansYTD }}

Trips with a reservation date in the current calendar year.

Total Managed Value

{{ maskVal('$' + totalRevenue) }}

Total USD value of all non-cancelled bookings.

Payment Pending

{{ paymentPendingTrips }}

Click to view trips with unsettled payment amounts.

Future Trips

{{ futureTrips }}

Click to view all future trips departing after today.

Scheduled Reminders

{{ remindersSummary.total }}

{{ reminderBadge.count }} {{ reminderBadge.label }}
Click to view all upcoming reminders and manage them.

Commissions YTD

{{ maskVal('$' + ytdCommission) }}

Sum of stored commission amounts from non-cancelled bookings with reservation dates in the current year. This is the full commission value, not the final agent payout.

Managed Value YTD

{{ maskVal('$' + managedValueYTD) }}

Total USD value of non-cancelled bookings whose Reservation Date is in the current year. Bookings without a Reservation Date or with a different year are excluded.

Total Commissions

{{ maskVal('$' + totalCommissions) }}

Sum of stored commission amounts from all non-cancelled bookings across all time. This is the full commission value, not the final agent payout.

Total Collected Commissions

{{ maskVal('$' + totalCollectedCommissions) }}

Sum of all collected commission values from non-cancelled records.

Recently Added Trips

New this week {{ recentTripsFreshness.count }} {{ recentTripsFreshness.delta >= 0 ? '+' : '' }}{{ recentTripsFreshness.delta }} vs last week

No recent trips yet

Added trips will appear here.

{{ trip.destination }}

Client: {{ maskName(trip.clientName) }}

Added {{ formatTripAddedDate(trip) }}

{{ amountsVisible ? formatMoney(trip.cost) : '••••••' }}

{{ trip.status }}
Monthly Sales Value Target {{ amountsVisible ? '$' + stats.monthlyCommission.toLocaleString() + ' / $' + stats.commissionTarget.toLocaleString() : '•••••• / ••••••' }}
USD
{{ selectedTripIds.size }} trip{{ selectedTripIds.size > 1 ? 's' : '' }} selected
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

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.

Redirecting to Trips in {{ importRedirectSeconds }}s
Redirect cancelled

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 }}

Number of non-cancelled trips matching the selected metric, year, and month filters.

Managed Value

{{ amountsVisible ? '$' + commGrossRevenue : '••••••' }}

Total booking value for the non-cancelled bookings included in the selected period.

Commission Earned

{{ amountsVisible ? '$' + commEarned : '••••••' }}

Commission amount for the selected metric and period. This is the full commission value, not the final agent payout.

Avg. Trip Value

{{ amountsVisible ? '$' + commAvgTripValue : '••••••' }}

Average booking value per non-cancelled trip in the selected period.

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 : '••••••' }}

{{ commVsPrior.isUp ? '+' : '' }}{{ commVsPrior.pct }}%

vs {{ commVsPrior.priorLabel }}

{{ commVsPrior.isUp ? '+' : '' }}{{ amountsVisible ? '$' + Math.abs(commVsPrior.diff).toLocaleString() : '••••••' }}

Select a year to compare
against the prior period.

Year
Month
Metric {{ commFilteredTrips.length }} trip{{ commFilteredTrips.length !== 1 ? 's' : '' }} match

Monthly Managed Value

Total booking value by month

{{ commFilterYears.length ? commFilterYears.join(', ') : 'All Years' }}{{ commFilterMonths.length ? ' · ' + commFilterMonths.map(m => ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][Number(m)-1]).join(', ') : '' }}
No data available for this period.
{{ row.label }}
{{ amountsVisible ? '$' + row.grossFormatted : '••••' }}
{{ amountsVisible ? '$' + row.grossFormatted : '••••••' }}
Total {{ amountsVisible ? '$' + formatNumber(commMonthlyBreakdown.reduce((s, r) => s + r.gross, 0), 2) : '••••••' }}

{{ commFilterMetric }}

{{ commFilterMetric === 'Destination' ? 'Top bookings by destination' : 'By month' }}

{{ commFilterYears.length ? commFilterYears.join(', ') : 'All Years' }}{{ commFilterMonths.length ? ' · ' + commFilterMonths.map(m => ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][Number(m)-1]).join(', ') : '' }}

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 || '—' }} — {{ 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.

{{ importLoadingLabel }}

Securely processing data

{{ importProgress }}%

Progress

{{ importProgress }}%

{{ importProcessedRows.toLocaleString() }} / {{ importTotalRows.toLocaleString() }} rows
{{ importSecondsLeft }}s left
{{ importUploadSpeedMb > 0 ? importUploadSpeedMb.toFixed(2) : '0.00' }} MB/s

{{ isEditing ? (editingType === 'trip' ? 'Edit Trip' : 'Edit Quote') : 'Add Quote' }}

{{ isEditing ? (editingType === 'trip' ? `Updating Trip #${editingId}` : `Updating Quote #${editingId}`) : 'Fill in the trip details for this quote' }}

••• •••••
+1 ••• ••• ••••
•••••@•••••
••••••
••••••
••••••

Customer access

Share trip data only during the travel window

Customer link

Edit Profile

Name & profile picture

Profile preview

Click photo to change

Profile is saved to your GCS bucket and loaded automatically on next login.

{{ profileError }}

{{ reminderViewMode ? 'Reminders (' + (tripReminderMap[reminderTrip?.id]?.length || 0) + ')' : 'New Reminder' }}

{{ maskName(reminderTrip.clientName || reminderTrip.client_name) }} • {{ reminderTrip.destination }}

Reminder saved!

You'll receive an email at the scheduled time.

{{ r.reminderAt?.split('T')[0] }} at {{ r.reminderAt?.split('T')[1]?.slice(0,5) }}
{{ r.email }}
{{ r.reason || 'No reason added' }}
Pending

{{ reminderError }}

{{ reminderError }}

{{ editingBookingId ? 'Edit Booking' : 'Add Booking' }}

{{ bookingModalTrip?.clientName }} · {{ bookingModalTrip?.destination }}

{{ getBookingPaymentStatus(newBookingForm) }}