From a3b8e9a4b0be794b87db7df3b578f909c1071d94 Mon Sep 17 00:00:00 2001 From: TopherMayor Date: Thu, 30 Apr 2026 11:53:18 -0700 Subject: [PATCH] feat: expand price automation contract --- README.md | 12 +-- price-watch/watch-targets.json | 143 ++++++++++++++++++++++++++--- public/index.html | 57 ++++++++++-- seed-data.js | 4 +- server.js | 158 ++++++++++++++++++++++++++------- 5 files changed, 316 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1ad4577..902282a 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,11 @@ node server.js ## Features - **Real-time WebSocket voting** — all clients update instantly -- **5 categories** — Hotels, Golf, Nightlife, Excursions, Full Itineraries -- **Budget planner tab** — quick compare for 8, 10, and 12 guys across Budget, Balanced, and Splurge tracks +- **6 planning categories** — Hotels, Flights, Golf, Nightlife, Excursions, and Full Itineraries +- **Budget planner tab** — compares 8, 10, and 12 guys across Budget, Balanced, and Splurge tracks - **Price trend graphs** — each option shows a live line graph from price-watch automation runs - **Source-selectable price tracking** — switch each option between Apple, Costco, KAYAK, and other tracked sources +- **Package vs standalone labels** — bundled flight+hotel quotes stay distinct from room-only, flight-only, tee-time, table, charter, and excursion prices - **Decision detail cards** — automation-enriched pricing, features, amenities, and tradeoffs appear on each option - **Add suggestions** — anyone can propose new venues - **Admin approval** — pending options require approval before going live @@ -26,11 +27,12 @@ node server.js ## Data Votes are stored in `data/votes.json` (created on first run). Edit directly or use the admin panel. -System seed data auto-refreshes researched package options and budget scenarios while preserving existing votes and user-added options. -Price-watch automation runs append time-series snapshots in `price-watch/history.jsonl`, which the app turns into per-option trend lines and decision detail cards. +System seed data auto-refreshes researched options while preserving existing votes and user-added options. +Price-watch automation runs append time-series snapshots in `price-watch/history.jsonl`, which the app turns into per-option trend lines and decision detail cards. Automation output should cover hotels, flights, golf, nightlife, and excursions, with `bookingType` and `priceBasis` separating package quotes from standalone booking prices. +When a run includes calculated `budgetScenarios` or `derivedItineraries`, the app uses those fresh automation calculations instead of the static seed budget scenarios. For hosted deployments, set `DATA_DIR` or `DATA_FILE` so mutable vote data lives outside the Git checkout. -When price-watch automation updates tracked data files in the repository, refresh the Ubuntu deployment so the hosted app picks up the latest option details and price history. +When price-watch automation updates tracked data files in the repository, commit/push those changes and refresh the Ubuntu deployment so the hosted app picks up the latest option details, price history, itinerary calculations, and budget scenarios. ## Deployment diff --git a/price-watch/watch-targets.json b/price-watch/watch-targets.json index 2888f3e..afa711f 100644 --- a/price-watch/watch-targets.json +++ b/price-watch/watch-targets.json @@ -10,35 +10,158 @@ }, "trackedSources": [ { - "id": "packages-hotels", - "label": "Packages and Hotels", - "categories": ["hotel"] + "id": "hotel-packages", + "label": "Flight + Hotel Packages", + "categories": ["hotel"], + "bookingType": "package", + "requiredChecks": [ + "Costco Travel package results", + "Apple Vacations package search", + "CheapCaribbean package search", + "other date-matched package providers found during research" + ] + }, + { + "id": "standalone-hotels", + "label": "Standalone Hotels", + "categories": ["hotel"], + "bookingType": "standalone", + "requiredChecks": [ + "KAYAK hotel search", + "official hotel booking engine when public rates are visible", + "other OTA hotel-only rates found during research" + ] + }, + { + "id": "flights", + "label": "Standalone Flights", + "categories": ["flight"], + "bookingType": "standalone", + "requiredChecks": [ + "LAX to SJD date-matched round trip", + "ONT to SJD date-matched round trip", + "capture airline, stops, schedule window, baggage caveats, and total price per traveler" + ] }, { "id": "golf", "label": "Golf", - "categories": ["golf"] + "categories": ["golf"], + "bookingType": "standalone", + "requiredChecks": [ + "official course tee-time pages when available", + "public tee-time marketplaces", + "resort/package golf inclusions when attached to hotel packages" + ] }, { "id": "nightlife", "label": "Nightlife and Day Clubs", - "categories": ["nightlife"] + "categories": ["nightlife"], + "bookingType": "standalone", + "requiredChecks": [ + "VIP table packages", + "bottle service minimums", + "day-club and beach-club package pricing", + "cover charges or ticketed events when visible" + ] }, { "id": "excursions", "label": "Excursions and Water Activities", - "categories": ["excursion"] + "categories": ["excursion"], + "bookingType": "standalone", + "requiredChecks": [ + "yacht and private charter quotes", + "whale-watch and sunset sail pricing", + "ATV/off-road packages", + "bachelor-party-relevant group excursions" + ] }, { - "id": "budget", - "label": "Budget Tracks", - "categories": ["budget"] + "id": "derived-itineraries", + "label": "Derived Itineraries", + "categories": ["itinerary"], + "bookingType": "calculated", + "requiredChecks": [ + "recalculate budget, balanced, and splurge itinerary totals from the current hotel/package, flight, golf, nightlife, and excursion results", + "include component breakdowns and assumptions for each itinerary" + ] + }, + { + "id": "derived-budgets", + "label": "Derived Budget Tracks", + "categories": ["budget"], + "bookingType": "calculated", + "requiredChecks": [ + "recalculate 8, 10, and 12 person totals from current results", + "prefer exact package prices when the itinerary uses a flight+hotel package", + "avoid double-counting flights or hotels already included in package prices", + "include per-person and group-total math plus assumptions" + ] } ], + "bookingTypeRules": { + "package": "Use for bundled products such as flight+hotel packages or hotel+transfer packages. Include included components and do not mix directly with standalone room-only rates.", + "standalone": "Use for individual bookings such as hotel-only rates, flights, golf tee times, nightlife tables, yacht charters, and excursions.", + "calculated": "Use for automation-derived itinerary and budget totals built from current package or standalone components." + }, + "outputSchema": { + "optionPrices": [ + { + "seedKey": "stable app option key when available", + "price": "numeric price only, or null when unavailable", + "displayLabel": "human-readable price label", + "category": "hotel | flight | golf | nightlife | excursion | itinerary | budget", + "source": "travel site or vendor", + "sourceUrl": "exact result or source URL", + "bookingType": "package | standalone | calculated", + "priceBasis": "perTraveler | perNight | perPerson | perGroup | totalPackage | perRound | perTable", + "includedComponents": ["flight", "hotel", "transfer", "golf", "nightlife", "excursion"], + "excludedComponents": ["components that must be budgeted separately"], + "origin": "airport code for flight/package quotes when applicable", + "destination": "airport or destination code when applicable", + "availability": "available | unavailable | sold-out | login-required | request-quote", + "features": ["structured decision features"], + "amenities": ["hotel or venue amenities"], + "inclusions": ["included items"], + "limitations": ["tradeoffs, caveats, restrictions"], + "decisionNote": "short decision note" + } + ], + "derivedItineraries": [ + { + "seedKey": "itinerary-budget | itinerary-balanced | itinerary-splurge | itinerary-concierge or new stable key", + "tier": "Budget | Balanced | Splurge | Concierge", + "perPerson": "numeric calculated per-person total", + "groupTotal": "numeric calculated group total when group size is known", + "groupSize": "8 | 10 | 12 or selected party size", + "components": ["component price keys used"], + "assumptions": ["calculation assumptions"], + "summary": "short recommendation summary" + } + ], + "budgetScenarios": [ + { + "id": "stable scenario id", + "tier": "Budget | Balanced | Splurge", + "groupSize": "numeric group size", + "perPerson": "numeric calculated per-person total", + "groupTotal": "numeric calculated group total", + "summary": "short scenario summary", + "notes": ["component breakdown and assumptions"] + } + ] + }, "notes": [ "Use seed-data.js as the current baseline for names, links, and budget assumptions.", + "Check hotels, flights, golf, nightlife, and excursions on every run before updating itinerary or budget recommendations.", + "Differentiate bundled package prices from standalone booking prices using bookingType and priceBasis on every price point.", + "For package quotes, list the included and excluded components so budgets do not double-count flights, hotels, transfers, or resort credits.", + "For standalone quotes, list the exact unit being priced: per night, per traveler, per person, per group, per round, or per table.", + "Itinerary and budget options are calculated outputs. Recompute them from the freshest current package and standalone component prices instead of treating seed-data.js totals as current.", "Write a human-readable report to price-watch/latest-report.md on every run.", - "Append one machine-readable summary line per run to price-watch/history.jsonl, including per-option price points keyed by stable option ids or seed keys.", + "Append one machine-readable summary line per run to price-watch/history.jsonl, including per-option price points, derivedItineraries, and budgetScenarios keyed by stable option ids or seed keys.", "Capture structured option details when available: current price, availability, source, sourceUrl, highlights, features, amenities, inclusions, limitations, and a short decision note.", "If a source is gated behind login or membership, note that clearly in both outputs." ] diff --git a/public/index.html b/public/index.html index eb61934..5513d66 100644 --- a/public/index.html +++ b/public/index.html @@ -329,6 +329,7 @@ --green: #34d399; --red: #f87171; --hotel: #3b82f6; + --flight: #38bdf8; --golf: #22c55e; --nightlife: #a855f7; --excursion: #06b6d4; @@ -835,6 +836,7 @@ width: 0%; } .vote-bar-fill.hotel { background: var(--hotel); } + .vote-bar-fill.flight { background: var(--flight); } .vote-bar-fill.golf { background: var(--golf); } .vote-bar-fill.nightlife { background: var(--nightlife); } .vote-bar-fill.excursion { background: var(--excursion); } @@ -1430,6 +1432,7 @@