Version 3.1 Β· August 2026
Rompo is a premium, mobile-first family travel planning app designed to coordinate multi-party trips β where multiple families, grandparents, or friend groups traveling from different cities converge on a shared destination.
"One trip. Many travelers. Zero chaos."
Rompo solves the #1 pain point of group travel: coordination across different travel parties with different ages, needs, origins, and schedules. Unlike generic itinerary apps, Rompo is built around the concept of Travel Parties β independent family units that each have their own flights, origins, and needs, but share a destination, accommodation, and activities.
Families planning vacations where multiple households travel together β ski trips, beach holidays, destination weddings, reunion trips, multi-generational travel.
Landing Page (4-step onboarding carousel)
ββ Page 0: Hero β "One trip. Many travelers. Zero chaos."
ββ Page 1: "Tell us who's coming along"
ββ Page 2: "AI plans the perfect day"
ββ Page 3: "Share with one tap" β [Get Started β It's Free]
ββ Redirects to Base44 auth
ββ Dashboard (first-time empty state)
ββ Onboarding Tour (5 steps)
ββ "Create First Trip" CTA
Key metrics to track: Landing β Sign-up conversion, Tour completion rate, Time to first trip creation.
Step 0: TRIP TYPE β What kind of trip?
ββ Two vibrant gradient cards:
β ββ π Friends β "One group, everyone flies from their own city"
β ββ π¨βπ©βπ§βπ¦ Families β "Multiple family groups, each flying from different cities"
ββ Selection determines Step 1 variant
ββ [Card tap β]
β
Step 1: WHO'S COMING? (branched by trip type)
β
ββ [Families mode β Step1Family]
β ββ Set trip name
β ββ Add travel parties (name, color, origin city/country)
β ββ Drag-to-reorder parties
β ββ Add members per party (name, age category, optional email)
β ββ Email lookup: checks if user exists in app
β ββ [Next β]
β
ββ [Friends mode β Step2Friends]
ββ Set trip name
ββ Add individual friends (name, origin city, optional email)
ββ Each friend becomes their own single-member party
ββ [Next β]
β
Step 2: WHERE & WHEN
ββ Search destination (Google Places Autocomplete)
ββ Set country (auto-filled from location search)
ββ Pick start & end dates
ββ Shows trip duration + party summary pills
ββ AI travel tip card (fetched on destination change)
ββ [Next β]
β
Step 3: TRAVEL β How Is Everyone Getting There?
ββ For each party: add transport legs (flight/train/bus/car/transfer/ferry)
ββ Each leg: from/to locations (with airport search), times, details
ββ Assign parties to shared legs
ββ "Everyone Meets Up" divider when independentβshared transition
ββ AI-suggested transfer times (optional LLM call)
ββ [Next β]
β
Step 4: STAY β Where Is Everyone Staying?
ββ Search accommodations via Google Places (or add manually)
ββ Accommodation cards with check-in/out dates/times
ββ Assign parties to accommodations
ββ Fallback to manual entry if Places API unavailable
ββ [Next β]
β
Step 5: PLAN β AI Itinerary Generation
ββ Set preferences (budget, pace, activities, accessibility, notes)
ββ AI consent modal (first-time only β persisted on user record)
ββ [Generate Itinerary β¨]
β ββ If trip.trip_pro is false β PaywallModal opens (gates AI generation)
β ββ If trip.trip_pro is true β LLM call β structured JSON response
ββ Day-by-day view with activity cards (time, title, description, age tag, icon)
ββ Fine-tune bar: More Rest / More Adventure / Kid-Friendly / Earlier / Later / More Food
ββ Regenerate with adjusted preferences
ββ [Next β] or [Skip for now]
β
Step 6: REVIEW & SAVE
ββ Trip summary header + timeline preview + sharing config
ββ Status selector (draft/planned/active/completed)
ββ [Save & Finish] β creates all entities β redirects to Trip page
ββ Handles both create and edit (cascading cleanup on edit)
ββ Sends invite emails to members with linked email addresses
Wizard persistence: Auto-saved to localStorage every 2s (debounced) via useWizardPersistence. Expires after 24h. Not restored when editing.
Edit mode: Accessed via ?edit=tripId query param. Loads all existing data into wizard for modification. Cleans up old entities before saving updated ones.
Progress bar: 6-step progress (Step 0 has no progress indicator). Steps are clickable to navigate back to any previously reached step.
Trip Page (/Trip?id=...)
ββ Header: back button, "Trip Details" title, Share + Invite buttons
ββ Offline Banner (when offline, shows cached data)
ββ Trip Header Card: gradient bg, title, destination, dates, countdown, party avatars
ββ Owner Controls: "Allow party editing" toggle
ββ Tab Navigation: Overview | Timeline | Activities | People
β
ββ [Overview Tab]
β ββ Travel Parties grid (2-col, color-coded, member count)
β ββ Trip Stats grid (flights, transfers, nights, activities β clickable)
β ββ Travelers list (name, age, age category badge)
β
ββ [Timeline Tab]
β ββ Grouped by date
β ββ Independent transport legs (party-specific)
β ββ "Everyone Meets Up" divider
β ββ Shared transport legs
β ββ Accommodation cards
β
ββ [Activities Tab]
β ββ AI-generated itinerary + activity management
β
ββ [People Tab]
ββ Party-grouped member cards with age categories
ββ Add/edit travelers per party
ββ Convergence view showing party merge points
Trip Owner clicks [+ Invite]
ββ Invite Modal opens
ββ Enter email address
ββ Select party to assign
ββ [Send Invite]
β ββ Creates InvitedTripUser record (status: pending)
β ββ Invites user to app via base44.users.inviteUser()
β ββ Sends email notification via Core.SendEmail
ββ Shows list of existing invites with status badges
Invited User receives email β Logs in β Opens Dashboard
ββ Trip appears in "Shared" tab
ββ Opens Trip page
ββ Auto-accepts pending invite
ββ Can view full trip
ββ If "allow_party_editing" is ON β can edit own party's legs
Trip Owner clicks [π Share]
ββ Creates/reuses ShareLink entity (unique token)
ββ Generates preview URL: /functions/sharePreview?token=...&lang=...
ββ Uses Web Share API (mobile) or clipboard (desktop)
ββ Share link flow:
ββ Messenger/social crawlers β GET request β HTML with OG meta tags
β ββ og:title, og:description, og:image (Unsplash travel photo)
ββ Users β auto-redirect to /SharedTrip?token=...&lang=...
ββ SharedTrip page (no auth required)
ββ Calls getSharedTrip backend function
ββ Renders read-only trip view
ββ Stats, parties, transport cards, accommodations
ββ "Plan your own β" CTA to Rompo
Dashboard
ββ Shows all trips (own + invited)
ββ Filter pills: All | Upcoming | Active | Past
ββ Each TripCard shows:
β ββ Status gradient header (draft/planned/active/completed)
β ββ Trip name, destination, dates
β ββ Progress text (days to go / day X of Y / completed)
β ββ Actions: View | Edit | Delete
ββ Pull-to-refresh support
ββ Offline mode with cached data
ββ Admin view toggle (admin users see all trips)
| Feature | Status | Priority | Notes | |---------|--------|----------|-------| | Multi-party trip creation | β Shipped | P0 | Core differentiator | | 7-step creation wizard (0β6) | β Shipped | P0 | Trip type selector + progress bar + auto-save | | Google Places search | β Shipped | P0 | Airports, cities, accommodations | | Transport leg management | β Shipped | P0 | Flight, train, bus, car, ferry, transfer | | Accommodation management | β Shipped | P0 | With party assignment | | AI itinerary generation | β Shipped | P1 | LLM-powered, age-aware | | Fine-tune itinerary | β Shipped | P1 | 6 adjustment presets | | Trip preferences | β Shipped | P1 | Budget, pace, activities, accessibility | | Share via link | β Shipped | P1 | OG tags for social preview | | Collaborative editing | β Shipped | P1 | Invite by email, party assignment | | Offline mode | β Shipped | P2 | localStorage caching | | Pull-to-refresh | β Shipped | P2 | Mobile UX | | Bilingual (EN/RU) | β Shipped | P2 | Full translation coverage | | Dark mode | β Shipped | P2 | System preference + manual toggle, deep navy palette | | Onboarding tour | β Shipped | P2 | Dashboard + Wizard tours | | Admin view | β Shipped | P3 | See all trips (admin role, hidden toggle) | | AI consent & age bucketing | β Shipped | P0 | Privacy-safe AI: age groups only, consent modal | | Sign in with Apple | β Shipped | P0 | Apple login via Base44 auth | | Privacy policy (expanded) | β Shipped | P0 | AI-Generated Content & Third-Party Login sections | | Multi-party analytics | β Shipped | P2 | Event tracking across wizard + sharing | | Design system tokens | β Shipped | P1 | Unified CSS variables across globals.css + index.css | | Trip type selector | β Shipped | P1 | Friends vs Families with vibrant gradient cards | | People tab | β Shipped | P1 | Full people management on Trip page | | Activities tab | β Shipped | P1 | AI itinerary display + activity management | | Public App Store launch | β Shipped | P0 | Live on iOS App Store GB | | Marketing landing page | β Shipped | P0 | /MarketingLanding β public-facing homepage | | TestFlight beta retirement | β Shipped | P0 | Signup flow removed; App Store only | | Google Search Console dashboard | β Shipped | P2 | Admin-only, OAuth connector, query analytics | | Trip Pro paywall | β Shipped | P1 | PaywallModal gates AI itinerary in Step 5; trip.trip_pro flag; price_variant A/B/C | | PDF export | π Planned | P2 | Gated behind Trip Pro | | Expense tracking | π Planned | P2 | Entity exists, no UI | | Real-time collaboration | π Planned | P3 | Entity subscriptions available | | Push notifications | π Planned | P3 | β |
User (built-in)
β
ββ creates β Trip
β ββ has many β TravelParty
β β ββ has many β Member
β β ββ assigned via β LegAssignment
β ββ has many β Leg (transport + accommodation)
β β ββ assigned via β LegAssignment
β ββ has many β ItineraryItem
β ββ has many β Activity
β ββ has many β Expense (entity exists, UI pending)
β ββ has many β ShareLink
β ββ has many β InvitedTripUser
β
ββ invited via β InvitedTripUser
| Field | Type | Required | Notes | |-------|------|----------|-------| | title | string | β | Trip name | | destination | string | β | City/region | | destination_country | string | | Country | | destination_lat/lng | number | | Coordinates | | start_date | date | β | Trip start | | end_date | date | β | Trip end | | status | enum | | draft, planned, active, completed | | emoji | string | | Destination emoji | | allow_party_editing | boolean | | Collaboration toggle | | trip_pro | boolean | | Trip Pro unlocked (gates AI itinerary, multi-family >2, PDF export) | | pro_purchased_at | datetime | | UTC timestamp of purchase (used for 7-day refund window) | | pro_stripe_session_id | string | | Stripe Checkout Session ID (cs_...) for idempotency | | requires_pro | boolean | | True when trip has features needing Pro (e.g. β₯3 parties); set client-side, does not block save | | price_variant | enum | | A=$9.99, B=$14.99, C=$19.99 β assigned via hash(trip_id) % 3 at paywall_viewed |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | name | string | β | "The Smiths" | | color | string | | Hex color | | origin_city/country | string | | Departure location | | origin_lat/lng | number | | Coordinates | | order | number | | Display order |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | party_id | string | β | FK to TravelParty | | name | string | β | Member name | | age | number | | Used for AI itinerary | | age_category | enum | | Toddler through Senior | | role | enum | | organizer, traveler, child | | user_id | string | | Linked app user |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | type | enum | β | flight, train, bus, car, transfer, ferry, accommodation, activity | | title | string | β | Leg name | | date | date | β | Date of leg | | start_time/end_time | string | | HH:MM format | | from_location/to_location | string | | Origin/destination names | | from_code/to_code | string | | Airport/station codes (IATA) | | from_lat/from_lng/to_lat/to_lng | number | | Coordinates | | duration | string | | e.g., "2h 15m" | | details/notes | string | | Additional info | | place_id | string | | Google Places ID | | phone/website/photo_url | string | | Accommodation details | | is_shared | boolean | | Shared across all parties | | order | number | | Sort order |
| Field | Type | Required | Notes | |-------|------|----------|-------| | leg_id | string | β | FK to Leg | | party_id | string | β | FK to TravelParty |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | day_number | number | β | Day of trip (1-indexed) | | time | string | | HH:MM | | title | string | β | Activity name | | description | string | | Details | | type | enum | | activity, meal, rest, transport | | age_tag | enum | | All Ages, Kids 4+, Teens+, etc. | | icon | string | | Emoji | | duration_minutes | number | | Duration | | order | number | | Sort within day |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | party_id | string | | Optional: limit to party view | | token | string | β | Unique share token | | is_active | boolean | | Can be deactivated |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | invited_email | string | β | Email of invitee | | invited_user_id | string | | Set when accepted | | party_id | string | | Assigned party | | status | enum | β | pending, accepted, declined, revoked | | permissions | array | | e.g., ["edit_own_legs"] |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | title | string | β | Activity name | | date | date | β | Date | | category | enum | | sightseeing, dining, adventure, etc. | | estimated_cost | number | | Cost estimate |
| Field | Type | Required | Notes | |-------|------|----------|-------| | trip_id | string | β | FK to Trip | | title | string | β | What for | | amount | number | β | Amount | | paid_by | string | β | Payer | | split_between | array | | Names for splitting | | category | enum | | accommodation, food, transport, etc. |
| Page | Route | Auth Required | Description | |------|-------|---------------|-------------| | Landing | / | No | Onboarding carousel, auto-redirect if logged in | | Dashboard | /Dashboard | Yes | Trip list, filters, management | | CreateTrip | /CreateTrip | Yes | 7-step wizard (0β6), edit via ?edit=id | | Trip | /Trip?id=... | Yes | Trip detail view with tabs | | Shared | /Shared | Yes | Trips shared with you | | SharedTrip | /SharedTrip?token=... | No | Public read-only trip view | | Profile | /Profile | Yes | Settings, language, dark mode, logout | | MasterDoc | /MasterDoc | Yes | Internal product documentation | | Support | /Support | Yes | Help & FAQ | | SupportTickets | /SupportTickets | Yes | Support ticket management | | Privacy | /Privacy | No | Privacy policy | | MarketingLanding | /MarketingLanding | No | Marketing page | | SearchConsole | /SearchConsole | Admin | Google Search Console analytics dashboard |
components/
βββ BottomNav.jsx β Fixed bottom navigation bar
βββ TripCard.jsx β Trip card for dashboard
βββ TripHeader.jsx β Destination photo / purple gradient trip header
βββ TransportLegCard.jsx β Dark navy transport card
βββ AccommodationCard.jsx β Dark navy accommodation card
βββ ActivityRow.jsx β Activity item with time bar
βββ ProgressBar.jsx β 6-step wizard progress (steps 1β6)
βββ InviteCollaboratorModal.jsx β Email invite modal
βββ TripShareSection.jsx β Share link generation
βββ PullToRefresh.jsx β Pull-to-refresh wrapper
βββ OfflineBanner.jsx β Offline notification
βββ AdminViewToggle.jsx β Admin/user view switch
βββ MobileSelect.jsx β Custom mobile select
βββ DeleteConfirmModal.jsx β Deletion confirmation
βββ SearchBottomSheet.jsx β Search overlay
βββ PageHeader.jsx β Generic page header
βββ DarkModeContext.jsx β Dark mode provider
βββ DarkModeToggle.jsx β Toggle button
βββ AiConsentModal.jsx β AI data disclosure consent
βββ paywall/
β βββ PaywallModal.jsx β Trip Pro upsell modal (shadcn Dialog, feature list, Stripe CTA placeholder)
βββ LanguageContext.jsx β i18n provider + translations
βββ useScrollRestore.js β Scroll position persistence
βββ useTabState.js β Tab state persistence (sessionStorage)
βββ useOfflineCache.js β Offline data caching
βββ useLocationSearch.js β Location search hook
β
βββ wizard/
β βββ Step0TripType.jsx β Friends vs Families selector
β βββ Step1Family.jsx
β βββ Step2Friends.jsx β Friends-mode party setup
β βββ Step2WhereWhen.jsx
β βββ Step3Travel.jsx
β βββ Step4Stay.jsx
β βββ Step5Plan.jsx
β βββ Step6Review.jsx
β βββ TripPreferences.jsx
β βββ WizardHeader.jsx β Step header with back + progress
β βββ WizardButton.jsx β Reusable wizard CTA button
β βββ WizardProgressDots.jsx β Dot-style progress indicator
β βββ useWizardPersistence.js β Auto-save wizard state to localStorage
β βββ PartyModal.jsx β Add/edit party bottom sheet
β βββ ValidationHint.jsx
β
βββ utils/
β βββ ageBucketing.js β Age-to-category + demographic summary
β
βββ tour/
β βββ TourOverlay.jsx
β βββ DashboardTourSteps.js
β βββ CreateTripTourSteps.js
β
βββ landing/
β βββ MountainScene.jsx
β βββ FamilyScene.jsx
β βββ ItineraryScene.jsx
β βββ ShareScene.jsx
β
βββ trip/ β Trip page sub-components
β βββ ActivitiesTab.jsx
β βββ PeopleTab.jsx
β βββ AddTravelerModal.jsx
β βββ ConvergenceView.jsx
β βββ PersonCard.jsx
β
βββ Shared* β SharedTrip sub-components
β βββ SharedTripHeader.jsx
β βββ SharedStatsGrid.jsx
β βββ SharedPartyCard.jsx
β βββ SharedTransportCard.jsx
β βββ SharedAccommodationCard.jsx
β βββ SharedActivityRow.jsx
β
βββ Timeline*
βββ TimelineAccommodationCard.jsx
| Pattern | Where Used | How | |---------|------------|-----| | Server state | Trip data, parties, members, legs | React Query with staleTime: 30-60s | | Local UI state | Modals, active tabs, form fields | useState | | Persisted UI state | Dashboard filter, scroll position | useTabState (sessionStorage) | | Scroll restoration | Dashboard, Shared, Profile | useScrollRestore (in-memory map) | | Offline cache | Trip data, dashboard trips | localStorage via useOfflineCache | | Auth state | Current user | useState + base44.auth.me() | | Language | EN/RU | LanguageContext + localStorage | | Dark mode | Light/Dark | DarkModeContext + localStorage |
Built on Base44 β a Backend-as-a-Service platform providing:
| Function | Auth | Purpose | Input | Output |
|----------|------|---------|-------|--------|
| searchLocations | No | Google Places Autocomplete for cities, airports, places | {term, types} | Array of location results |
| autocompletePlaces | No | Places autocomplete | {input} | Predictions |
| getPlaceDetails | No | Google Place details | {place_id} | Place details with lat/lng |
| searchPlaces | No | General places search | {query} | Place results |
| getSharedTrip | No* | Fetch trip data by share token | {token} | Full trip + parties + legs + itinerary |
| sharePreview | No* | OG meta tags + redirect for share links | GET: ?token=&lang= | HTML with OG tags |
| generateSecureToken | Yes | Crypto-secure share token generation | {} | {token} |
| getMyInvitedTrips | Yes | Fetch trips user was invited to | {} | {trips: [...]} |
| inviteTripUser | Yes | Send trip invitation email | {tripId, email, partyId} | Status |
| submitSupportRequest | Yes | Submit support ticket | {name, email, category, message} | {ticket_ref} |
| searchConsoleSites | Admin | List verified Google Search Console sites | {} | {sites: [...]} |
| searchConsoleQueries | Admin | Fetch top search queries for a site | {siteUrl, days, rowLimit} | {rows: [...]} |
*Uses service role (base44.asServiceRole) since these are public endpoints.
| Secret | Purpose | |--------|---------| | GOOGLE_PLACES_API_KEY | Google Places API for location search |
base44.functions.invoke('functionName', params){data, status, headers}base44.asServiceRole.entities.*base44.entities.*Aesthetic: Inspired by "App in the Air" β dark navy boarding-pass-style cards for transport, warm cream backgrounds, card-based layouts, mobile-first.
| Usage | Font | Weight | Notes | |-------|------|--------|-------| | Body text | DM Sans | 300β700 | All UI text | | Numbers, codes, times | DM Mono | 400β500 | Airport codes, countdown, dates |
Light Mode:
| Token | Value | Usage |
|-------|-------|-------|
| --rompo-bg | #F8F7F4 | Page background (warm cream) |
| --rompo-surface | #FFFFFF | Card backgrounds |
| --rompo-primary | #410472 | Primary purple |
| --rompo-primary-light | #8825F0 | Purple light |
| --rompo-accent | #E94560 | Red accent |
| --rompo-text | #1A1A2E | Primary text |
| --rompo-text-secondary | #6B7280 | Secondary text |
| --rompo-border | #E5E7EB | Borders |
Dark Mode:
| Token | Value | Usage |
|-------|-------|-------|
| --rompo-bg | #0F172A | Deep navy background |
| --rompo-surface | #1E293B | Card backgrounds |
| --rompo-surface-secondary | #1A2332 | Input backgrounds |
| --rompo-primary | #A855F7 | Purple primary |
| --rompo-primary-light | #C084FC | Purple light |
| --rompo-primary-pale | #3B0764 | Dark purple tint |
| --rompo-accent | #FB7185 | Pink accent |
| --rompo-text | #F1F5F9 | Light text |
| --rompo-text-secondary | #94A3B8 | Secondary text |
| --rompo-text-muted | #64748B | Muted text |
| --rompo-border | #334155 | Borders |
| --rompo-border-light | #1E293B | Subtle borders |
Party Colors: Pink (#DB2777), Teal (#0E7490), Coral (#F97316), Green (#10B981), Blue (#3B82F6), Amber (#F59E0B)
| Token | Value | |-------|-------| | Page padding | 20px | | Card radius | 16px | | Large card radius | 20px | | Pill radius | 20px | | Input radius | 12px | | Card padding | 16px 20px | | Card gap | 10px |
LanguageContext with React ContextlocalStorage("appLanguage")t(key, params) with placeholder support ({minutes}, {partyName})createTranslator(lang) for non-context pages (SharedTrip)~150 translation keys covering all UI text. All user-facing text goes through the t() function.
LanguageContext.jsxsharePreview function for OG tag translationsuseOfflineCache.js)useOnlineStatus() hook (browser online/offline events)| State | Dashboard | Trip Page | |-------|-----------|-----------| | Online | Live data from React Query | Live data, cached in background | | Offline | Shows cached trip list | Shows cached trip data | | Returning online | Auto-refreshes (staleTime triggers) | Auto-refreshes |
| Role | View Trip | Edit Trip | Edit Legs | Invite Others | Delete Trip | |------|-----------|-----------|-----------|---------------|-------------| | Owner | β | β | β | β | β | | Invited (accepted) | β | β | Own party only* | β | β | | Share link viewer | Read-only | β | β | β | β | | Not authorized | β | β | β | β | β |
*Only when allow_party_editing is enabled by the owner.
is_active: false)created_by_id === user.id OR user.role === "admin"invited_email === user.email (so invitees can see/accept their own invites)created_by_id only (no admin override)user.role === "admin" checkgoogle_search_console)webmasters.readonly, userinfo.email, openidsearchConsoleSites (list verified properties) + searchConsoleQueries (fetch query analytics)| Endpoint | Usage | Backend Function | |----------|-------|-----------------| | Autocomplete | City, airport, and place search | searchLocations | | Place Details | Lat/lng, address, IATA codes | searchLocations (inline) | | Place Autocomplete | Accommodation search | autocompletePlaces | | Place Details | Hotel details, photos | getPlaceDetails |
API Key: Stored as GOOGLE_PLACES_API_KEY secret.
ageToCategory() converts exact ages to groups (e.g. "2 adults, 1 child") before sending to AI. User must consent via AiConsentModal before first generation. Consent is persisted on user record (ai_consent_given).| Event | Properties | Location | Business Value |
|-------|-----------|----------|----------------|
| party_added | party_count | Step1Family | Multi-party adoption |
| wizard_step1_completed | party_count, total_members, members_with_email | Step1Family | Funnel: party setup completion |
| travel_leg_added | total_legs | Step3Travel | Transport planning engagement |
| wizard_step3_completed | total_legs, shared_legs, independent_legs, party_count | Step3Travel | Funnel: transport step + shared vs independent ratio |
| wizard_step4_completed | total_accommodations, shared_accommodations, party_count | Step4Stay | Funnel: accommodation step + sharing patterns |
| itinerary_generated | days_count, total_items, had_adjustment, party_count | Step5Plan | AI adoption + fine-tune engagement |
| collaborator_invited | trip_id, has_party | InviteCollaboratorModal | Collaboration adoption |
| share_link_copied | trip_id, share_type (full_trip / party_view) | TripShareSection | Viral coefficient + party-specific sharing |
| Event | Properties | Business Value |
|-------|-----------|----------------|
| trip_created | party_count, member_count, duration_days | Core conversion |
| invite_accepted | β | Invite conversion |
| shared_trip_viewed | β | Share link effectiveness |
| language_changed | from, to | i18n usage |
| dark_mode_toggled | mode | Preference data |
| wizard_draft_saved | step_number | Engagement signal |
The public-facing marketing page at /MarketingLanding is accessible without authentication and serves as the primary acquisition channel.
Sections (top to bottom):
| Component | Purpose |
|-----------|---------|
| MarketingNav | Fixed dark nav with Rompo logo + App Store CTA button |
| MarketingHero | Full-height dark section with headline, subtext, floating emojis, boarding pass visual, CTA buttons |
| DemoSection | Interactive phone mockup with tabbed Transport / Stays / Itinerary demo |
| ProblemSection | Problem/solution framing for group travel pain points |
| HowItWorks | 3-step explainer (Add families β Plan together β Share) |
| DifferentiatorsGrid | Feature grid highlighting multi-party, AI, sharing, offline |
| SharePreviewSection | Share link preview demonstration |
| FinalCTA | Purple gradient CTA section with App Store button |
| MarketingFooter | Links to Privacy + Support, copyright |
All App Store CTAs point to: https://apps.apple.com/gb/app/rompo-travel/id6760403260
TestflightSignup component renders null (preserved as no-op to avoid import errors)TestflightSignup entity is retained in the database schema but no longer used/MarketingLanding are automatically redirected to /Dashboard?preview=1 URL param bypasses the redirect (for internal review)searchLocations trusts all input?tab=timeline)generateSecureToken backend functionSecurity Hardening & iOS Viewport Optimization
paddingTop: calc(20px + env(safe-area-inset-top)) to respect the iOS status bar / notch. viewport-fit=cover added to index.html to enable safe-area insets.user-select: text for copy-paste on mobile, while UI controls remain non-selectable.<select> dropdowns in Step1Family (age category picker) and Step5Plan with touch-friendly bottom-sheet MobileSelect components for better mobile UX.Trip Pro Paywall System
trip.trip_pro boolean flag on the Trip entity. When true, AI itinerary generation, multi-family support (>2 parties), PDF export, and dynamic OG images are unlocked for that trip.src/components/paywall/PaywallModal.jsx component using shadcn Dialog. Displays 4 feature bullets (AI-powered daily plan, up to 6 families, beautiful share previews, printable PDF), a primary teal gradient CTA button ("Unlock for $14.99"), a "Maybe later" dismiss, and a 7-day money-back guarantee footer.trip.trip_pro before invoking the LLM. If false, PaywallModal opens instead of starting AI generation.price_variant (A=$9.99, B=$14.99, C=$19.99) assigned deterministically via hash(trip_id) % 3 at paywall_viewed for future A/B testing.pro_purchased_at (UTC datetime for 7-day refund window), pro_stripe_session_id (Stripe cs_... for idempotency), requires_pro (client-set flag when β₯3 parties added).Logo Integration & Collaboration Features Shipped
Country Field Fix & Backend Reliability
Auto-Save on Step Navigation & UX Cleanup
?edit=tripId), the wizard now automatically saves the full trip state to the database every time the user advances to a new step. Previously, data was only persisted when the user reached Step 6 and clicked "Save & Finish", meaning navigating away mid-edit could lose changes.goTo triggers save β The goTo(step, dataOverride) function in CreateTrip was updated to call saveTripToDB automatically. Step handlers (handlePeopleDone, handleWhereWhenDone, etc.) now pass their latest state as dataOverride to ensure the most up-to-date data (not a stale closure) is persisted.BottomNav previously showed a window.confirm() dialog when the user tried to navigate away from an active edit session. With consistent auto-saving now in place, this confirmation is redundant and has been removed for a smoother UX.Navigation Stack Fix
CreateTrip navigated to /Trip?id=... without replacing the /CreateTrip entry in the browser history stack. This caused the Back button on the Trip page to return to the wizard editor (instead of Dashboard), and the Trips tab in BottomNav to restore /CreateTrip as the last stack entry.handleComplete in CreateTrip now uses navigate(..., { replace: true }) when in edit mode, replacing /CreateTrip in history so the stack reads /Dashboard β /Trip rather than /Dashboard β /CreateTrip β /Trip.Step1Family and Step2Friends "Back" buttons in edit mode now navigate directly to /Trip?id=... with replace: true (previously called navigate(-1), which could land anywhere in browser history).Google Search Console Integration
google_search_console via Base44 app connector (scopes: webmasters.readonly, userinfo.email, openid).searchConsoleSites backend function β Admin-only endpoint; fetches list of verified properties via the Google Search Console API using the stored connector access token.searchConsoleQueries backend function β Admin-only endpoint; fetches top search queries for a given site URL with configurable date range (7 / 28 / 90 days) and row limit./SearchConsole page β Admin-only analytics dashboard displaying site selector, date range filter, summary metric cards (clicks, impressions, CTR, position), and a ranked query table with per-row performance bars.QA Bug Fix Sprint
ValidationHint on party name in Step1Family with a ValidationHintTouched component that only shows the error after the user has typed and then cleared the party name field. The "Next" button was already correctly gated on tripName.trim() && parties.some(p => p.name.trim()) β no origin city, country, or member records required.navigator.clipboard.writeText() in TripShareSection with try/catch and added a textarea + execCommand('copy') fallback for browsers/WebViews where the Clipboard API is restricted (e.g. in-app browsers on iOS).enabled: !!user. No structural change required; auth state is properly awaited before API calls fire.DarkModeToggle is correctly wired to toggleDark from context, has a 48Γ44px tap target, and uses role="switch" with aria-checked. No change required.Public App Store Launch & Marketing
TestflightSignup component renders null; entity retained for data history.MarketingHero, FinalCTA, MarketingNav).?preview=1 param bypasses for internal review.Design System Unification & UI Polish
globals.css and index.css to use identical light-mode (--background: 39 18% 97% / warm cream #F8F7F4) and dark-mode (deep navy #0F172A) HSL tokens. Eliminated token drift between the two files.--rompo-surface-secondary, --rompo-primary-pale, --rompo-text-secondary, --rompo-text-muted, --rompo-border, --rompo-border-light, and enhanced party/accent colors for dark contexts.PeopleTab component on Trip page showing party-grouped travelers with add/edit/convergence views.ActivitiesTab component rendering AI-generated itinerary with activity management.?tab=timeline) for shareable deep links.-webkit-autofill overrides.border-color: #410472, box-shadow: 0 0 0 3px rgba(65, 4, 114, 0.12)) applied to all inputs.Apple App Store Compliance
ageToCategory() utility converts exact ages to demographic groups (toddler, child, teen, adult, senior) before sending to LLM. Full names are never sent.AiConsentModal prompts users before first AI itinerary generation. Consent persisted on user record (ai_consent_given). Includes privacy policy link and provider disclosure.Multi-Party Analytics Tracking
base44.analytics.track() across all multi-party planning touchpointsDesign System & UI Hardening
rgba(0,0,0,0.5) for reliable dimming across light and dark modesDark Mode
Collaboration & Sharing
MasterDoc
Trip Cards