Bali App β€” Antigravity Build Guide
🏝 Bali Trip App

Antigravity Build Guide

Every session, every prompt, in order. Follow this exactly and you will ship a working app.

9 Sessions React + Supabase 5 Travelers 11–15 Apr 2026
Before You Start
Prepare Everything First
Do all of this before opening Antigravity. Going in unprepared means you'll stop mid-session to get keys and lose context.
CRITICAL
Step 1 β€” Prep Your Figma Code Files
Do this before anything else
βŒ„
Find and replace in your Figma HTML files before uploading
Find β†’ Replace with: Alex Rivera β†’ Andrew Elena Chen β†’ Carol Marcus Vogt β†’ Wei2 Sarah Blake β†’ Mayvipah Julian Fox β†’ Chaaanok USD β†’ IDR $ β†’ Rp July 12 β†’ April 11 July 20 β†’ April 15 2024 β†’ 2026
⚠️
If you skip this, Antigravity will build with the wrong names and currency throughout. Fix it in the source before uploading β€” not after.
ACCOUNTS
Step 2 β€” Create All Accounts
All free tiers β€” do these in parallel
βŒ„
Supabase

Your database + realtime + file storage.
supabase.com
Create project named bali-trip, region: Singapore

Google Cloud

Maps + Distance Matrix.
console.cloud.google.com
Enable: Maps JS API, Distance Matrix API, Geocoding API

OpenAI

GPT-4o for place data extraction.
platform.openai.com
Create API key with billing enabled

Open Exchange Rates

Daily IDR/MYR/THB rates.
openexchangerates.org
Free plan, no credit card

AviationStack

Flight status tracking.
aviationstack.com
Free plan: 100 req/month

KEYS
Step 3 β€” Collect All Keys Into One File
Save this β€” you'll paste from it into every session
βŒ„
Create a text file called keys.txt and fill these in
SUPABASE_URL=https://YOUR_PROJECT.supabase.co SUPABASE_ANON_KEY=eyJh... GOOGLE_MAPS_KEY=AIza... OPENAI_API_KEY=sk-... OPEN_EXCHANGE_APP_ID=... AVIATIONSTACK_KEY=... # Fill these after running Supabase user seed (Session 1): ANDREW_ID= CAROL_ID= WEI2_ID= MAYVIPAH_ID= CHAAANOK_ID=
REFERENCE
Step 4 β€” Image Reference Map
Paste this at the start of every Antigravity session
βŒ„
Your 15 design screens
Image 1
Add Expense form
Image 2
Add Flight form
Image 3
+ Add Menu Modal
Image 4
Add to Itinerary form
Image 5
Add to Wishlist form
Image 6
Edit Itinerary item
Image 7
Flight Status detail
Image 8
Activity Detail view
Image 9
Itinerary Timeline
Image 10
Login / User Select
Image 11
Expenses overview
Image 12
Map view + filters
Image 13
Move to Itinerary picker
Image 14
Wishlist item detail
Image 15
Wishlist list view
Session 1 of 9
Project Setup + Database
Do not build any UI yet. This session creates the project shell, connects Supabase, runs the schema, and seeds the 5 users. Everything else depends on this being correct.
UPLOAD FIRST
What to upload before typing anything
Attach these files to your first message
βŒ„
  • 1
    All your Figma HTML/CSS export files (already find-replaced)
  • 2
    All 15 design screenshots (Images 1–15)
  • 3
    Your keys.txt file (Supabase URL + anon key only for now)
πŸ’‘
Upload all files in the very first message. Antigravity will reference them throughout all sessions without you needing to re-upload.
PROMPT
Session 1 β€” Full Prompt to Paste
Copy this exactly
βŒ„
Paste this as your first message (with files attached)
I am building a shared trip planner web app called "Bali Trip" for 5 travelers. I have attached: 1. My Figma design export files (HTML/CSS) β€” read these to understand the full design system, component styles, colours, typography, and spacing. Do not build anything yet. 2. Design screenshots (Images 1–15) β€” reference map is below. 3. My credentials file. IMAGE REFERENCE MAP: Image 1 = Add Expense form Image 2 = Add Flight form Image 3 = + Add Menu Modal Image 4 = Add to Itinerary form Image 5 = Add to Wishlist form Image 6 = Edit Itinerary item form Image 7 = Flight Status detail Image 8 = Activity/Place Detail view Image 9 = Itinerary Timeline (main view) Image 10 = Login / User Selection screen Image 11 = Expenses overview + settlement Image 12 = Map view with filter bar Image 13 = Move Wishlist item to Itinerary Image 14 = Wishlist item detail card Image 15 = Wishlist list view TECH STACK: React + Tailwind CSS, Supabase (database + realtime + edge functions). TRIP CONSTANTS (hardcode these everywhere): - Trip name: Bali Trip - Dates: 11 April 2026 to 15 April 2026 (5 days) - Travelers: Andrew (MYR), Carol (MYR), Wei2 (MYR), Mayvipah (THB), Chaaanok (THB) - Default travel mode: Car / Driving only. No scooter, no walking options anywhere. - All prices entered in IDR. Displayed as "Rp 150.000" format. - No $ signs, no price tier symbols ($$$ etc) anywhere in the app. SUPABASE CREDENTIALS: URL: [paste your URL] Anon Key: [paste your anon key] YOUR TASK FOR THIS SESSION: 1. Set up the React + Tailwind project connected to Supabase 2. Run this exact SQL schema in Supabase β€” do not modify any field names: CREATE TABLE users ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, currency text NOT NULL, pin_hash text, created_at timestamptz DEFAULT now() ); CREATE TABLE fx_rates ( id serial PRIMARY KEY, base text NOT NULL, target text NOT NULL, rate numeric NOT NULL, fetched_at timestamptz DEFAULT now() ); CREATE TABLE itinerary_items ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, address text, opening_hours text, cost_idr integer, image_url text, category text, status text DEFAULT 'wishlist', trip_day integer, start_time time, duration_mins integer, sort_order integer, source_url text, is_time_block boolean DEFAULT false, time_block_label text, is_done boolean DEFAULT false, added_by uuid REFERENCES users(id), created_at timestamptz DEFAULT now() ); CREATE TABLE votes ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), item_id uuid REFERENCES itinerary_items(id) ON DELETE CASCADE, user_id uuid REFERENCES users(id), UNIQUE(item_id, user_id) ); CREATE TABLE activity_attendance ( item_id uuid REFERENCES itinerary_items(id) ON DELETE CASCADE, user_id uuid REFERENCES users(id), PRIMARY KEY (item_id, user_id) ); CREATE TABLE comments ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), item_id uuid REFERENCES itinerary_items(id) ON DELETE CASCADE, user_id uuid REFERENCES users(id), text text NOT NULL, created_at timestamptz DEFAULT now() ); CREATE TABLE expenses ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), description text, amount_idr integer NOT NULL, paid_by uuid REFERENCES users(id), split_between uuid[] NOT NULL, category text, created_at timestamptz DEFAULT now() ); CREATE TABLE flights ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid REFERENCES users(id), flight_number text, airline text, departure_date date, departure_time time, terminal text, gate text, data_json jsonb, last_updated timestamptz DEFAULT now() ); CREATE TABLE accommodation ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), property_name text, address text, checkin_time timestamptz, checkout_time timestamptz, host_name text, host_phone text, updated_by uuid REFERENCES users(id), updated_at timestamptz DEFAULT now() ); ALTER PUBLICATION supabase_realtime ADD TABLE itinerary_items; ALTER PUBLICATION supabase_realtime ADD TABLE expenses; ALTER PUBLICATION supabase_realtime ADD TABLE votes; ALTER PUBLICATION supabase_realtime ADD TABLE activity_attendance; ALTER PUBLICATION supabase_realtime ADD TABLE comments; 3. Seed the 5 users: INSERT INTO users (name, currency) VALUES ('Andrew', 'MYR'), ('Carol', 'MYR'), ('Wei2', 'MYR'), ('Mayvipah', 'THB'), ('Chaaanok', 'THB'); 4. Show me the 5 user UUIDs from the users table after seeding. 5. Do NOT build any UI components yet. Confirm when done and show me the UUIDs.
⚠️
After this session, copy the 5 UUIDs into your keys.txt file before starting Session 2.
Session 2 of 9
Auth + App Shell + Navigation
Build the login screen and the 4-tab navigation shell. No feature tabs need to work yet β€” just the structure and auth flow.
PROMPT
Session 2 β€” Full Prompt
Auth screen + app shell
βŒ„
Paste this as your Session 2 opening message
Session 1 is complete. The database and schema are set up. Now build Session 2: Auth and App Shell. USER IDs (from Supabase users table): Andrew: [paste UUID] Carol: [paste UUID] Wei2: [paste UUID] Mayvipah: [paste UUID] Chaaanok: [paste UUID] DESIGN REFERENCE: Image 10 (Login / User Selection screen) BUILD EXACTLY THIS: 1. LOGIN SCREEN (match Image 10 layout): - Header: "Ready for Bali?" in large bold type - Subtext: "Select your profile to continue" - 5 user avatar cards in a grid: Andrew, Carol, Wei2, Mayvipah, Chaaanok - Each card has a avatar illustration (use simple initials avatar if no image), name below - Tapping a card opens a PIN entry overlay 2. PIN ENTRY OVERLAY: - 4 large dot circles showing PIN progress - Number pad (1–9, then 0) below - First visit for this user: "Set your PIN" β€” ask to enter twice to confirm - Return visit: "Welcome back, [name]" β€” verify PIN against stored hash - Wrong PIN: shake animation on dots, show "Incorrect PIN" - No lockout, no forgot PIN - Use bcrypt or SHA-256 to hash the PIN before storing in Supabase users table 3. ON SUCCESSFUL LOGIN: - Store in React session state (NOT localStorage): user_id, user_name, home_currency - Navigate to the main app 4. MAIN APP SHELL: - Bottom navigation bar with 4 tabs: Itinerary (map icon), Wishlist (heart icon), Expenses (wallet icon), Flights (plane icon) - Match the nav bar style from Image 9 bottom - Each tab shows an empty placeholder screen for now: "[Tab name] coming soon" - Active tab highlighted in blue - App header shows "Bali Trip" + user avatar top right 5. FLOATING + BUTTON: - Blue circular + button bottom right (above nav bar) - Tapping it opens the Add Menu Modal (Image 3) with 4 options: - Add to Wishlist - Add to Itinerary - Add Expense - Add Flight - These options do nothing yet β€” just close the modal. They will be wired up in later sessions. Do not build any tab content yet. Just auth + shell + nav + floating button.
Session 3 of 9
Wishlist Tab
The full wishlist β€” card list, category filters, add form with GPT place lookup, voting, comments, and the Move to Itinerary flow.
PROMPT
Session 3 β€” Full Prompt
Complete wishlist tab
βŒ„
Paste this as your Session 3 opening message
Auth and shell are working. Now build Session 3: the full Wishlist tab. DESIGN REFERENCES: Image 15 (list view), Image 14 (item detail), Image 5 (add form), Image 13 (move to itinerary picker) OPENAI API KEY: [paste key] SUPABASE URL + KEY: [paste both] BUILD EXACTLY THIS: 1. WISHLIST LIST VIEW (Image 15): - "Your saved gems." heading - 4 category filter pills across top: All Items Β· Stay Β· Food Β· Activities Β· Spa - Tapping a filter shows only that category - Cards sorted by vote count desc, then newest first - Each card shows: full-width image (or placeholder), category badge top-right, name, brief notes preview, vote count - Floating "+ Add Item" button bottom right β€” opens Add to Wishlist form 2. ADD TO WISHLIST FORM (Image 5): - Header image banner: teal gradient with "Capture a new memory" text - Category selector: 4 icon tiles β€” Stay (bed icon), Food (fork icon), Activities (hiking icon), Spa (leaf icon). Selected = blue filled tile. - Fields: - Location Name (text input, placeholder "e.g., La Favela Bali") - Address/Link (text input, placeholder "Google Maps link or street address") - Estimated Price (number input in IDR β€” shown as "Rp" prefix) - Notes (textarea, placeholder "Anything special to remember?") - When user types in Address/Link and taps a "Search" icon button next to the field: - Call a Supabase Edge Function named "lookup-place" - Edge Function calls GPT-4o with the input and returns JSON: { name, address, opening_hours, image_url } - Pre-fill Name, Address, and opening_hours fields with the result - Set image_url on the card - If GPT returns null or call fails: show toast "Could not find place β€” please fill in manually" - "Add to Wishlist" button at bottom β€” saves to itinerary_items table with status='wishlist' - Syncs to all phones instantly via Supabase Realtime 3. WISHLIST ITEM DETAIL (Image 14): - Full-width image at top with category badge overlay - Name + price (Rp amount, right-aligned) - Vote row: "X/5 travelers voted for this" with vote button - Notes box (grey background) - 3 buttons at bottom: "Add to Itinerary" (blue), "Edit Wishlist" (outline), "Delete Wishlist" (red text) 4. VOTING RULES: - Food & Drink and Spa: heart button per person, 1 per item. Shows "3/5 ❀️". Green highlight at 3+. - Activities: 5 name pills (Andrew, Carol, Wei2, Mayvipah, Chaaanok). Each person toggles only their own. Filled = in, outline = out. - Stay: same as Food hearts, but card shows green border + "βœ“ Majority reached" at 3+. - Only the logged-in user can toggle their own vote/attendance. 5. COMMENTS (πŸ’¬ Notes button on each card): - Opens a slide-up panel - Shows all comments: author name, text, time ago - Text input + Post button at bottom - Append-only (no delete, no edit) - Real-time sync via Supabase Realtime on comments table 6. MOVE TO ITINERARY (Image 13): - Opens a modal with place image, place name, "Add [name] to Itinerary" heading - Date picker: Day 1 (11 Apr) through Day 5 (15 Apr) β€” shown as "Apr 11, 2026" format - Time picker: hour:minute AM/PM - "Confirm & Add to Trip" blue button - On confirm: updates itinerary_items β€” set status='confirmed', trip_day, start_time - Item immediately appears in Itinerary tab Do not build the Itinerary tab yet. Just make the Move to Itinerary flow complete the database write.
Session 4 of 9
Itinerary Tab β€” Timeline + Stay
The main itinerary timeline view with travel times between stops, done check-off, conflict alerts, drag to reorder, the Stay sub-tab, and the place detail screen.
PROMPT
Session 4 β€” Full Prompt
Timeline + Stay sub-tabs
βŒ„
Paste this as your Session 4 opening message
Wishlist is working. Now build Session 4: the Itinerary tab with Timeline and Stay sub-tabs. DESIGN REFERENCES: Image 9 (timeline main view), Image 8 (place detail), Image 4 (add to itinerary form), Image 6 (edit form) GOOGLE MAPS KEY: [paste key] SUPABASE URL + KEY: [paste both] BUILD EXACTLY THIS: 1. ITINERARY TAB STRUCTURE: - 3 sub-tabs inside Itinerary tab: Timeline Β· Stay Β· Map - Map sub-tab = placeholder "Coming in next session" - Default sub-tab = Timeline 2. TIMELINE SUB-TAB (Image 9): - App header shows "Vacation in Bali", "11 April – 15 April", "5 Travelers" - Day selector: horizontal scrollable row of day pills β€” MON 11, TUE 12, WED 13, THU 14, FRI 15 - Active day = blue filled pill - Default = current date if within trip range, else Day 1 - TIMELINE LAYOUT: - Vertical timeline with time stamps on the left (e.g. 09:45 AM) - Category icon circle on the timeline line (coloured by category) - Item card to the right of the icon showing: - Name (bold) - Subtitle / address line - Thumbnail image (right side of card) - βœ… Done button (tap to mark done β€” grey out card, strikethrough name, move to bottom) - πŸ’¬ comment badge with count - Done counter at top of day: "2 of 5 done" - TRAVEL CONNECTOR between consecutive items: - Row between two cards: πŸš— X km β€’ Y mins - Pull from Google Distance Matrix API (driving mode ONLY β€” no other modes) - Use address field of each item as origin/destination - Call once per day load, cache in session state - If address missing: "Add address to calculate" - If API fails: "Travel time unavailable" - CONFLICT ALERT: - Red ⚠️ badge on item card if start_time is before opening hours start OR end time (start + duration) is after opening hours end - Tapping badge shows tooltip with specific message - DRAG TO REORDER: - Long-press to drag items within the same day - Updates sort_order in database on drop - Does NOT auto-recalculate times - TIME BLOCK (lightweight): - Appears on timeline in lighter grey style - No image, no voting, no conflict check - Shows label + time only - FLOATING + BUTTON on Timeline: - Opens Add to Itinerary form (Image 4): - Header banner (teal gradient "Capture a new memory") - Category selector: Stay Β· Food Β· Activities Β· Spa (icon tiles) - Location Name field - Address/Link field (same GPT lookup as wishlist β€” reuse lookup-place edge function) - Price field (IDR, Rp prefix) - Date picker (Day 1–5 = Apr 11–15) - Time picker - Notes textarea - "Add to Itinerary" button (blue) - "Save to Wishlist" text link below (saves with status='wishlist' instead) 3. PLACE DETAIL SCREEN (Image 8): - Triggered by tapping any item card on the timeline - Full-width image at top - Category badge overlay on image - Name (large bold) - Address with map pin icon (tappable β€” opens Google Maps) - Date & Time info box + Price info box (side by side) - Notes section (full text) - Travel Logistics section: - "From previous: [previous item name]" β€” Distance + Travel time - "To next: [next item name]" β€” Distance + Travel time - Both use Google Distance Matrix (driving) - 2 buttons at bottom: "Edit Item" (outline), "Open Maps" (blue) 4. EDIT ITINERARY ITEM (Image 6): - Same form layout as Add to Itinerary - Pre-filled with existing data - 3 actions at bottom: - "Save Changes" (blue button) - "Move to Wishlist" (blue outline, heart icon) β€” sets status back to 'wishlist' - "Remove from Itinerary" (red text, trash icon) β€” deletes the item 5. STAY SUB-TAB: - Single card for the whole group - Fields: Property name, Full address, Check-in date + time, Check-out date + time, Host name, Host phone (tapping opens WhatsApp) - Large "πŸ“‹ Copy Address for Grab" button β€” copies address to clipboard - Anyone can edit - If empty: show "Add Accommodation" button - Save to accommodation table in Supabase
Session 5 of 9
Map Sub-tab
Full-screen Google Map inside the Itinerary tab with category filters, colour-coded pins, tap-to-view place cards, and the distance measure mode.
PROMPT
Session 5 β€” Full Prompt
Map sub-tab with distance measure
βŒ„
Paste this as your Session 5 opening message
Timeline and Stay tabs are working. Now build Session 5: the Map sub-tab inside the Itinerary tab. DESIGN REFERENCE: Image 12 (map view with filter bar and pin detail) GOOGLE MAPS KEY: [paste key] SUPABASE URL + KEY: [paste both] BUILD EXACTLY THIS: 1. MAP SUB-TAB (Image 12): - Replace the "Coming soon" placeholder in the Map sub-tab - Full-screen Google Maps JavaScript API map - Bali, Indonesia as default centre (lat: -8.4095, lng: 115.1889), zoom 11 2. FILTER BAR (pinned at top of map): - Pill buttons: All Β· Wishlist Β· Itinerary Β· Stay Β· Food Β· Activities Β· Spa - Active filter = blue filled pill (default = All) - Filters can be combined (e.g. Itinerary + Spa) - Tapping a filter updates which pins are shown on the map 3. PINS: - Green pin = confirmed itinerary item (status='confirmed') - Yellow pin = wishlist item (status='wishlist') - Each pin uses a circular thumbnail image if image_url exists, else a coloured dot with category icon - All pins matching the active filter are shown simultaneously 4. TAPPING A PIN: - Opens a bottom sheet sliding up from the bottom - Bottom sheet shows: image, name, category badge, address, opening hours, cost in "Rp X.XXX" - Comment count badge - 2 buttons: "View in Timeline/Wishlist" (jumps to that tab and item) and "πŸ“ Measure Distance" 5. MEASURE DISTANCE MODE: - After tapping "πŸ“ Measure Distance" on a pin: - Map enters measure mode - Blue banner at top: "Tap another pin to measure distance" - User taps any other pin on the map - App draws a route polyline between the two points - Result card appears at bottom: πŸ“ [Place A] β†’ [Place B] πŸš— X mins β€’ Y km β€’ Car - Pulled from Google Distance Matrix API (driving mode ONLY) - "Clear" button dismisses the result and exits measure mode 6. MY LOCATION: - β—Ž location button at bottom-right of map - Tapping centres map on browser geolocation position - Shows blue dot at user's position - Updates every 30 seconds while map is open - If permission denied: show toast "Location not available" 7. ZOOM CONTROLS: - + and βˆ’ buttons on the right side of the map (Image 12 style)
Session 6 of 9
Expenses Tab
Full expense ledger with IDR input, home currency display, category breakdown, settlement engine, and WhatsApp export.
PROMPT
Session 6 β€” Full Prompt
Bali-Split ledger + settlement
βŒ„
Paste this as your Session 6 opening message
Map is working. Now build Session 6: the full Expenses tab. DESIGN REFERENCES: Image 11 (expenses overview), Image 1 (Add Expense form) SUPABASE URL + KEY: [paste both] CURRENCY RULES β€” READ CAREFULLY: - Andrew = MYR, Carol = MYR, Wei2 = MYR - Mayvipah = THB, Chaaanok = THB - ALL expenses entered in IDR - Display each expense in the logged-in user's home currency using rates from fx_rates table - NEVER call a live currency API on render β€” always read from fx_rates table - Cross-currency settlement (Thai person owes Malaysian person): show both "ΰΈΏ 1,450 (β‰ˆ RM 38.20)" BUILD EXACTLY THIS: 1. EXPENSES OVERVIEW (Image 11): - Header card (dark blue): "Total Trip Spending" in large text showing total IDR converted to current user's currency - Two sub-cards: "I OWE" (blue card) and "OTHERS OWE ME" (teal card) β€” calculated from settlement engine - FX rate display row: "1 IDR = 0.000285 MYR Β· 0.0023 THB Β· Updated X hours ago" + Refresh button - Top Categories breakdown: Food, Transport, Stay, Activities, Spa β€” each with progress bar and total - Recent Expenses list (grouped by date: TODAY, YESTERDAY, etc.) - Each expense row: category icon, description, amount in IDR + home currency equivalent, paid by, time 2. ADD EXPENSE FORM (Image 1): - "New Expense" header with back arrow, "Drafts" text right - "Total Amount" label - Large IDR input field β€” shows "IDR 150.000" as user types (dot-separated thousands) - Below input: "Approx. [Rp X in home currency]" in small grey text (from fx_rates table) - Category selector pills (horizontal scroll): Food Β· Transport Β· Stay Β· Activities Β· Spa Β· Other β€” selected = green filled - "Split with..." section: - Toggle: "Split Equally" (default) | "Certain Users" - In "Split Equally" mode: shows all 5 users with checkbox (all checked), each showing "IDR X share" - In "Certain Users" mode: uncheck to exclude - Shows "Sarah, Julian & 2 more" style summary for unchecked users (use actual names) - Notes field (optional): placeholder "Dinner at the night market..." - "+ Add Expense" blue button at bottom - Save to expenses table: amount_idr, paid_by (current user), split_between (array of checked user IDs), category, description 3. SETTLEMENT TAB (sub-tab inside Expenses alongside expense list): - "Calculate Settlement" button - Runs minimum-transactions algorithm (NOT just net balances): 1. Calculate net balance per person (total paid βˆ’ total owed) 2. Separate into creditors (positive) and debtors (negative) 3. Greedily match largest debtor to largest creditor until all zeroed - Output format: Mayvipah β†’ Andrew RM 120.50 Chaaanok β†’ Carol RM 88.00 Chaaanok β†’ Wei2 RM 34.00 - MYR for transfers involving Andrew/Carol/Wei2 - THB for transfers involving Mayvipah/Chaaanok - Cross-currency: show both amounts - "Export" button generates plain text and copies to clipboard: Bali Trip Settlement β€” 15 Apr 2026 Mayvipah pays Andrew RM 120.50 Chaaanok pays Carol RM 88.00 Chaaanok pays Wei2 RM 34.00 Generated by Bali Trip App 4. REALTIME: - Subscribe to expenses table β€” new expenses appear instantly on all phones without refresh
Session 7 of 9
Flights Tab
Per-person flight tracking with manual entry and AviationStack status lookup via Edge Function.
PROMPT
Session 7 β€” Full Prompt
Flight tracker tab
βŒ„
Paste this as your Session 7 opening message
Expenses are working. Now build Session 7: the Flights tab. DESIGN REFERENCES: Image 2 (Add Flight form), Image 7 (Flight Status detail) AVIATIONSTACK KEY: [paste key] SUPABASE URL + KEY: [paste both] BUILD EXACTLY THIS: 1. FLIGHTS TAB MAIN VIEW: - Header: "Flights" with compass icon top right - One row per traveler: Andrew, Carol, Wei2, Mayvipah, Chaaanok - Each row shows: - User avatar + name - If no flight saved: "Add flight" prompt (grey, tappable) - If flight saved: flight number + status badge (On Time green / Delayed yellow / Landed grey / Unknown grey) - Tapping a row with a saved flight opens the Flight Status detail view - Tapping "Add flight" opens the Add Flight form 2. ADD FLIGHT FORM (Image 2): - "Add Flight" header, back arrow, user avatar top right - FLIGHT NUMBER field (text, e.g. GA-402) with plane icon - AIRLINE field (text, e.g. Garuda Indonesia) with building icon - DEPARTURE DATE (date picker, mm/dd/yyyy format) - DEPARTURE TIME (time picker) - FLIGHT DETAILS section (labelled "Optional"): - TERMINAL field - GATE field - "Add to Itinerary" blue button β€” saves flight to flights table and calls the status Edge Function - Small text below button: "Adding a flight will automatically update your Itinerary and notify you of any schedule changes." 3. SUPABASE EDGE FUNCTION: get-flight-status - Proxies call to AviationStack API (prevents CORS error in browser) - AviationStack free plan uses HTTP not HTTPS β€” the Edge Function handles this server-side - Input: flight_number - Output: status, departure airport, arrival airport, departure time, arrival time, terminal, gate, duration - Saves result to flights.data_json in Supabase 4. FLIGHT STATUS DETAIL (Image 7): - User avatar + "Voyager" app name top - "Flight Status" label + status badge (e.g. "● On Time" in green pill) - Descriptive text: "Your journey to [destination] starts in X days." - Flight card: - Flight number (bold, blue, e.g. FLIGHT VY-204) - Route: [DEP] β†’ [ARR] airport codes - Departure section: time (large bold), "DEPARTURE" label, airport name, Terminal + Gate tags - Flight duration (centre, clock icon, e.g. "13H 30M DURATION") - Arrival section: time (large bold), "+1 DAY" badge if applicable, "ARRIVAL" label, airport name, Terminal/Carousel tags - Confirmation code, Seat Assignment, Cabin Class, Status β€” 2x2 grid at bottom of card - "Manage My Booking" blue button at bottom 5. AUTO-REFRESH: - Refresh flight status every 10 minutes while Flights tab is open - Manual refresh button per flight row - Update flights.data_json and flights.last_updated in Supabase on each refresh
Session 8 + 9 of 9
FX Cron + Final Polish
Two final sessions β€” FX rate automation and then a full polish pass fixing any visual or functional gaps before launch.
PROMPT
Session 8 β€” FX Cron Job
Keeps currency rates updated daily
βŒ„
Paste this as your Session 8 opening message
Flights are working. Now build Session 8: the FX rate cron job. OPEN EXCHANGE RATES APP ID: [paste ID] SUPABASE URL + KEY: [paste both] BUILD EXACTLY THIS: 1. SUPABASE EDGE FUNCTION: update-fx-rates - Calls Open Exchange Rates API URL: https://openexchangerates.org/api/latest.json?app_id=[ID]&base=USD&symbols=IDR,MYR,THB - Calculate cross rates: IDR→MYR = MYR_rate / IDR_rate, IDR→THB = THB_rate / IDR_rate - Upsert into fx_rates table: - Row 1: base='IDR', target='MYR', rate=[calculated], fetched_at=now() - Row 2: base='IDR', target='THB', rate=[calculated], fetched_at=now() - Log success/failure with timestamp 2. SUPABASE CRON JOB: - Schedule: every day at 00:00 UTC - Calls the update-fx-rates Edge Function - Name it: "daily-fx-update" 3. SEED INITIAL RATES (run once now so the app works immediately): INSERT INTO fx_rates (base, target, rate) VALUES ('IDR', 'MYR', 0.000285), ('IDR', 'THB', 0.0023); (These are approximate — the cron will overwrite with real rates at midnight) 4. WIRE UP THE REFRESH BUTTON: - The "Refresh" button in the Expenses tab header calls the update-fx-rates Edge Function on demand - After the function completes, re-fetch fx_rates from Supabase and update all displayed currency conversions - Show a loading spinner on the button while running, then "Updated just now" text
PROMPT
Session 9 β€” Full Polish Pass
Fix everything before sharing with your group
βŒ„
Paste this as your Session 9 opening message
FX cron is working. This is the final polish session before launch. Go through the entire app and fix the following: 1. DESIGN CONSISTENCY: - Every screen must match the Figma designs I uploaded (Images 1–15) - Font sizes, spacing, card shadows, border radii, colours must be consistent throughout - Bottom nav bar style must be identical on every tab - All prices must show "Rp X.XXX" format (dot as thousands separator) β€” never "$" or "IDR X,XXX" - No placeholder "Coming soon" text should remain anywhere 2. MOBILE RESPONSIVENESS: - Test every screen at 390px wide (iPhone 14 viewport) - Nothing should overflow horizontally - All tap targets minimum 44px height - Bottom nav must not overlap content 3. REALTIME SYNC CHECK: - Confirm Supabase Realtime subscriptions are active on: itinerary_items, expenses, votes, activity_attendance, comments - When any user adds/changes data, other users see it within 2 seconds without refresh 4. EDGE CASES TO HANDLE: - Empty state on Timeline: "No plans yet for Day X β€” add from Wishlist or use the + button" - Empty state on Wishlist: "No saved places yet β€” tap + to add your first" - Empty state on Expenses: "No expenses yet β€” tap + to log your first" - Empty state on Flights: each row shows "Add flight" prompt - GPT lookup failure: shows manual entry form with toast notification - AviationStack failure: shows "Flight status unavailable β€” check airline app" - No internet: show "You're offline β€” showing last saved data" 5. NAVIGATION WIRING: - "View in Timeline/Wishlist" from map opens correct tab + scrolls to correct item - "Add to Wishlist" from + modal opens Wishlist form - "Add to Itinerary" from + modal opens Itinerary form - "Add Expense" from + modal opens Expense form - "Add Flight" from + modal opens Flight form - All back arrows navigate correctly 6. FINAL CHECK β€” run through this full user flow once: - Login as Andrew β†’ add a place to Wishlist β†’ vote on it β†’ move it to Itinerary Day 2 β†’ check the timeline β†’ mark it Done β†’ add an expense β†’ check settlement β†’ add a flight β†’ view flight status - Everything should work end to end with no errors in console
Launch
Pre-Launch Checklist
Do this after all 9 sessions are complete. Test on real phones before sharing the link with your group.
CHECKLIST
Test With 2 Real Phones First
You + one friend β€” before sharing with everyone
βŒ„
  • β–‘
    Login as Andrew on your phone, login as Carol on a friend's phone simultaneously
  • β–‘
    Add a wishlist item on Andrew's phone β€” confirm it appears on Carol's phone within 2 seconds
  • β–‘
    Add an expense on Carol's phone β€” confirm it appears on Andrew's phone and the IDR converts to MYR correctly
  • β–‘
    Move a wishlist item to Itinerary Day 2 β€” confirm it appears on the timeline on both phones
  • β–‘
    Check the settlement engine with at least 3 expenses β€” verify the maths manually
  • β–‘
    Tap the map, confirm all added places appear as pins, tap a pin, confirm the detail card shows correctly
  • β–‘
    Use Measure Distance between 2 pins β€” confirm travel time shows in Car mode
  • β–‘
    Add a flight number β€” confirm status fetches correctly
  • β–‘
    Add accommodation address β€” confirm "Copy for Grab" copies the right text
  • β–‘
    Check the app on iOS Safari β€” "Share β†’ Add to Home Screen" for app-like experience
  • β–‘
    Check the app on Android Chrome β€” menu β†’ "Add to Home Screen"
  • β–‘
    Check no console errors in browser DevTools
SHARING
How to Onboard Your 4 Friends
Send this via WhatsApp
βŒ„
WhatsApp message to send
Hey everyone! Our Bali trip app is ready 🏝 Link: [your app URL] 1. Open the link on your phone 2. Tap your name 3. Set a 4-digit PIN (you'll use this every time) 4. On iPhone: tap Share β†’ "Add to Home Screen" so it works like an app 5. On Android: tap menu β†’ "Add to Home Screen" We'll use this to plan the itinerary, split expenses, and track flights. Don't lose your PIN β€” I can reset it if needed.
API TABLE
Full API Reference
All services used in the app
βŒ„
APIPurposeCalled From
GPT-4o (OpenAI)Place name/address/hours/image extractionSupabase Edge Function: lookup-place
Google Maps JS APIMap display + pin renderingFrontend
Google Distance MatrixCar travel time between locationsFrontend + Edge Function
AviationStackFlight status dataSupabase Edge Function: get-flight-status
Open Exchange RatesDaily IDR→MYR and IDR→THB ratesSupabase Edge Function: update-fx-rates
Browser GeolocationUser's current location on mapFrontend only
Supabase RealtimeLive sync across all 5 phonesFrontend subscriptions