Files
cabo-voting-app/seed-data.js
2026-06-12 13:18:43 -07:00

805 lines
35 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const SEED_VERSION = 7;
const PRICE_UPDATED_AT = '2026-05-01';
const CATEGORY_META = {
hotel: { emoji: '🏨', color: '#3b82f6' },
flight: { emoji: '✈️', color: '#38bdf8' },
golf: { emoji: '⛳', color: '#22c55e' },
nightlife: { emoji: '🎧', color: '#a855f7' },
excursion: { emoji: '🚤', color: '#06b6d4' },
itinerary: { emoji: '🗺️', color: '#fbbf24' },
budget: { emoji: '💸', color: '#f97316' },
results: { emoji: '🏆', color: '#facc15' },
};
const GUEST_ROSTER = [
{ name: 'Jon', last4: '7506', role: 'groom' },
{ name: 'Toph', last4: '8116', role: 'best-man' },
{ name: 'Hans', last4: '6681', role: 'guest' },
{ name: 'Janno', last4: '2809', role: 'guest' },
{ name: 'JT', last4: '3286', role: 'guest' },
{ name: 'Cordero', last4: '0379', role: 'guest' },
{ name: 'Lester', last4: '8014', role: 'guest' },
{ name: 'Nick', last4: '6044', role: 'guest' },
{ name: 'David', last4: '5993', role: 'guest' },
{ name: 'Poalo', last4: '9922', role: 'guest' },
{ name: 'Justin', last4: '2329', role: 'guest' },
{ name: 'Ben Stewart', last4: '1957', role: 'guest' },
{ name: 'Joseph', last4: '4976', role: 'guest' },
{ name: 'Francis', last4: '4934', role: 'guest' },
];
const BUDGET_SCENARIOS = [
{
id: 'budget-8',
tier: 'Budget',
groupSize: 8,
perPerson: 1405,
groupTotal: 11240,
summary: 'Corazon + Palmilla + one shared activity + one nightlife night',
notes: [
'Flight estimate: $350',
'Hotel estimate: $450',
'Golf: Palmilla from $130',
'Activity: public sail / whale watch about $95',
'Food + drinks buffer: $275',
'Private round-trip transfer share: about $33',
],
},
{
id: 'budget-10',
tier: 'Budget',
groupSize: 10,
perPerson: 1398,
groupTotal: 13980,
summary: 'Corazon + Palmilla + one shared activity + one nightlife night',
notes: [
'Flight estimate: $350',
'Hotel estimate: $450',
'Golf: Palmilla from $130',
'Activity: public sail / whale watch about $95',
'Food + drinks buffer: $275',
'Private round-trip transfer share: about $26',
],
},
{
id: 'budget-12',
tier: 'Budget',
groupSize: 12,
perPerson: 1392,
groupTotal: 16704,
summary: 'Corazon + Palmilla + one shared activity + one nightlife night',
notes: [
'Flight estimate: $350',
'Hotel estimate: $450',
'Golf: Palmilla from $130',
'Activity: public sail / whale watch about $95',
'Food + drinks buffer: $275',
'Private round-trip transfer share: about $22',
],
},
{
id: 'balanced-8',
tier: 'Balanced',
groupSize: 8,
perPerson: 1688,
groupTotal: 13504,
summary: 'Grand Fiesta Americana + golf + sunset sail + one nightlife night',
notes: [
'Flight estimate: $350',
'All-inclusive stay: $850',
'Golf: Cabo del Sol / similar from $180',
'Sunset sail: about $125',
'Nightlife + covers: $100',
'Transfer + resort buffer: about $83',
],
},
{
id: 'balanced-10',
tier: 'Balanced',
groupSize: 10,
perPerson: 1681,
groupTotal: 16810,
summary: 'Grand Fiesta Americana + golf + sunset sail + one nightlife night',
notes: [
'Flight estimate: $350',
'All-inclusive stay: $850',
'Golf: Cabo del Sol / similar from $180',
'Sunset sail: about $125',
'Nightlife + covers: $100',
'Transfer + resort buffer: about $76',
],
},
{
id: 'balanced-12',
tier: 'Balanced',
groupSize: 12,
perPerson: 1677,
groupTotal: 20124,
summary: 'Grand Fiesta Americana + golf + sunset sail + one nightlife night',
notes: [
'Flight estimate: $350',
'All-inclusive stay: $850',
'Golf: Cabo del Sol / similar from $180',
'Sunset sail: about $125',
'Nightlife + covers: $100',
'Transfer + resort buffer: about $72',
],
},
{
id: 'splurge-8',
tier: 'Splurge',
groupSize: 8,
perPerson: 2484,
groupTotal: 19872,
summary: 'Breathless or Secrets + premium golf + private charter + VIP table',
notes: [
'Flight estimate: $400',
'Upscale all-inclusive stay: $1250',
'Premium golf: Quivira / similar about $250',
'Private whale or charter share: about $188',
'VIP nightlife share: about $213',
'Transfers + premium dinner buffer: about $183',
],
},
{
id: 'splurge-10',
tier: 'Splurge',
groupSize: 10,
perPerson: 2346,
groupTotal: 23460,
summary: 'Breathless or Secrets + premium golf + private charter + VIP table',
notes: [
'Flight estimate: $400',
'Upscale all-inclusive stay: $1250',
'Premium golf: Quivira / similar about $250',
'Private whale or charter share: about $150',
'VIP nightlife share: about $170',
'Transfers + premium dinner buffer: about $126',
],
},
{
id: 'splurge-12',
tier: 'Splurge',
groupSize: 12,
perPerson: 2289,
groupTotal: 27468,
summary: 'Breathless or Secrets + premium golf + private charter + VIP table',
notes: [
'Flight estimate: $400',
'Upscale all-inclusive stay: $1250',
'Premium golf: Quivira / similar about $250',
'Private whale or charter share: about $125',
'VIP nightlife share: about $142',
'Transfers + premium dinner buffer: about $122',
],
},
];
function buildOptionImageUrl(option) {
if (option.imageUrl) return option.imageUrl;
const primaryUrl = option.links?.[0]?.url || option.url || option.bookingUrl || '';
return primaryUrl ? `/api/preview-image?url=${encodeURIComponent(primaryUrl)}` : null;
}
function createOption(option) {
const categoryColor = CATEGORY_META[option.categoryId]?.color || '#888';
const primaryUrl = option.links?.[0]?.url || option.url || null;
return {
approved: true,
addedBy: 'system',
votes: [],
details: [],
links: [],
categoryColor,
url: primaryUrl,
bookingUrl: option.bookingUrl || primaryUrl,
imageUrl: buildOptionImageUrl(option),
...option,
categoryColor,
url: primaryUrl,
bookingUrl: option.bookingUrl || primaryUrl,
imageUrl: buildOptionImageUrl(option),
};
}
function buildSeedData() {
return {
seedVersion: SEED_VERSION,
priceUpdatedAt: PRICE_UPDATED_AT,
categories: [
{ id: 'hotel', name: 'Hotels', emoji: '🏨' },
{ id: 'flight', name: 'Flights', emoji: '✈️' },
{ id: 'golf', name: 'Golf', emoji: '⛳' },
{ id: 'nightlife', name: 'Nightlife', emoji: '🎧' },
{ id: 'excursion', name: 'Excursions', emoji: '🚤' },
{ id: 'itinerary', name: 'Itineraries', emoji: '🗺️' },
{ id: 'budget', name: 'Budget', emoji: '💸' },
{ id: 'results', name: 'Results', emoji: '🏆' },
],
guestRoster: GUEST_ROSTER,
budgetScenarios: BUDGET_SCENARIOS,
options: [
createOption({
id: 'hotel-corazon',
seedKey: 'hotel-corazon',
categoryId: 'hotel',
name: 'Corazon Cabo Resort & Spa',
desc: 'Best party-first base on Medano Beach. Walkable to downtown and Costco package pages currently show transfer-inclusive offers plus 4th or 5th night promos.',
lat: 23.0639,
lng: -109.6991,
details: ['Costco package availability only', 'KAYAK no fresh rates', 'Walk to marina nightlife'],
links: [
{ label: 'Official', url: 'https://www.corazoncabo.com/' },
{ label: 'Costco Travel', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSCORAZON20230510' },
{ label: 'KAYAK', url: 'https://www.kayak.com/Cabo-San-Lucas-Hotels-Cabo-Villas-Beach-Resort-Spa.58565.ksp' },
],
}),
createOption({
id: 'hotel-breathless',
seedKey: 'hotel-breathless',
categoryId: 'hotel',
name: 'Breathless Cabo San Lucas',
desc: 'Adults-only, marina-facing, and the easiest all-inclusive pick for a true bachelor-party vibe.',
lat: 23.0628,
lng: -109.6981,
details: ['Apple exact-date quote: $2,016 pp', 'Costco package: $1,678.99 pp', 'Adults-only'],
links: [
{ label: 'Official', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/breathless/mexico/cabo-san-lucas-resort-spa/' },
{ label: 'Hyatt', url: 'https://www.hyatt.com/en-US/hotel/mexico/breathless-cabo-san-lucas/brcsl' },
{ label: 'Costco Travel', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSBREATHE20230330' },
{ label: 'Apple Vacations', url: 'https://www.applevacations.com/hotels/breathless-cabo-san-lucas-resort-spa-all-inclusive-adults-only?cachefaretype=&destinationcode=SJD&dynamicpackageid=H01&los=4&mode=0&onsaleid=1398047&traveldate=2027-02-02' },
],
}),
createOption({
id: 'hotel-grand-fiesta',
seedKey: 'hotel-grand-fiesta',
categoryId: 'hotel',
name: 'Grand Fiesta Americana Los Cabos',
desc: 'Best overall balance for golf + all-inclusive + quality. Strong fit if the group wants one easy answer without going full splurge.',
lat: 23.0949,
lng: -109.7067,
details: ['Apple exact-date quote: $2,111 pp', 'KAYAK from $212/night', 'Golf-friendly'],
links: [
{ label: 'Official', url: 'https://www.fiestamericanatravelty.com/en/grand-fiesta-americana/hotels/grand-fiesta-americana-los-cabos-all-inclusive-golf-and-spa' },
{ label: 'Costco Travel', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSGRANDFIESTA20171011' },
{ label: 'Apple Vacations', url: 'https://www.applevacations.com/HotelById?cachefaretype=&destinationcode=SJD&dynamicpackageid=H01&hotelcode=43000&los=4&mode=0&onsaleid=1614780&redirect=false&remotesourcecode=HBSHotel&traveldate=2027-02-02&vendorcode=APV' },
{ label: 'KAYAK', url: 'https://www.kayak.com/Cabo-San-Lucas-Hotels-Grand-Fiesta-Americana-Los-Cabos-Golf-Spa.331383.ksp' },
],
}),
createOption({
id: 'hotel-secrets',
seedKey: 'hotel-secrets',
categoryId: 'hotel',
name: 'Secrets Puerto Los Cabos',
desc: 'Upscale adults-only pick with strong group-trip polish. Better for a luxe weekend than a chaos-first party hotel.',
lat: 23.0227,
lng: -109.7062,
details: ['KAYAK exact-date room rate: $335/night', 'Costco package: $2,005.80 pp', 'Adults-only'],
links: [
{ label: 'Official', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/secrets/mexico/puerto-los-cabos-golf-spa-resort/' },
{ label: 'Costco Travel', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSSECRETSPUERT20230330' },
{ label: 'CheapCaribbean', url: 'https://www.cheapcaribbean.com/HotelById/?cachefaretype=&destinationcode=SJD&dynamicpackageid=H01&hotelcode=SJDSCRT&los=4&mode=0&onsaleid=2173329&redirect=false&remotesourcecode=LtmsHotel&traveldate=2027-02-02&vendorcode=CCV' },
{ label: 'KAYAK', url: 'https://www.kayak.com/San-Jose-del-Cabo-Hotels-Secrets-Puerto-Los-Cabos-Adults-Only.551846.ksp' },
],
}),
createOption({
id: 'hotel-pacifica',
seedKey: 'hotel-pacifica',
categoryId: 'hotel',
name: 'Pueblo Bonito Pacifica',
desc: 'Luxury adults-only option with Quivira access. Best if the trip is really a premium golf weekend with nightlife as a side quest.',
lat: 23.0474,
lng: -109.7053,
details: ['Adults-only', 'Quivira access', 'Luxury retreat'],
links: [
{ label: 'Official', url: 'https://www.pueblobonito.com/resorts/pacifica?resort=4' },
{ label: 'Quivira FAQ', url: 'https://www.pueblobonito.com/resorts/pacifica/faq' },
],
}),
createOption({
id: 'hotel-dreams-los-cabos',
seedKey: 'hotel-dreams-los-cabos',
categoryId: 'hotel',
name: 'Dreams Los Cabos Suites Golf Resort & Spa',
desc: 'Balanced all-inclusive option with the cleanest Apple and Costco pricing signal from today.',
details: ['Apple exact-date quote: $1,757 pp', 'Costco package: $1,447.80 pp', 'All-inclusive'],
links: [
{ label: 'Hyatt Inclusive', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/dreams/mexico/los-cabos-suites-golf-resort-spa/' },
],
}),
createOption({
id: 'hotel-zoetry-casa-del-mar',
seedKey: 'hotel-zoetry-casa-del-mar',
categoryId: 'hotel',
name: 'Zoetry Casa del Mar',
desc: 'Higher-end adults-only pick that sits in the luxe tier without going fully maxed out.',
details: ['Apple exact-date quote: $1,944 pp', 'Costco package: $1,717.42 pp', 'Adults-only'],
links: [
{ label: 'Hyatt', url: 'https://www.hyatt.com/en-US/hotel/mexico/zoetry-casa-del-mar/zocdm' },
],
}),
createOption({
id: 'hotel-hyatt-ziva-los-cabos',
seedKey: 'hotel-hyatt-ziva-los-cabos',
categoryId: 'hotel',
name: 'Hyatt Ziva Los Cabos',
desc: 'Family-friendly luxury option that still works for a big group if the trip tilts more polished than rowdy.',
details: ['Apple exact-date quote: $2,178 pp', 'Beachfront', 'All-inclusive'],
links: [
{ label: 'Hyatt', url: 'https://www.hyatt.com/en-US/hotel/mexico/hyatt-ziva-los-cabos/sjdif' },
],
}),
createOption({
id: 'hotel-riu-palace-cabo-san-lucas',
seedKey: 'hotel-riu-palace-cabo-san-lucas',
categoryId: 'hotel',
name: 'Riu Palace Cabo San Lucas',
desc: 'Value-forward all-inclusive with a more party-friendly profile than the luxury adults-only resorts.',
details: ['Apple exact-date quote: $1,529 pp', 'Party-friendly all-inclusive', 'Value pick'],
links: [
{ label: 'RIU', url: 'https://www.riu.com/en/hotel/mexico/los-cabos/hotel-riu-palace-cabo-san-lucas/' },
],
}),
createOption({
id: 'hotel-riu-palace-baja-california',
seedKey: 'hotel-riu-palace-baja-california',
categoryId: 'hotel',
name: 'Riu Palace Baja California',
desc: 'Adults-only RIU choice with a cleaner energy than Cabo San Lucas while staying in the value band.',
details: ['Apple exact-date quote: $1,597 pp', 'Adults-only', 'RIU all-inclusive'],
links: [
{ label: 'RIU', url: 'https://www.riu.com/en/hotel/mexico/los-cabos/hotel-riu-palace-baja-california/' },
],
}),
createOption({
id: 'hotel-me-cabo-by-melia',
seedKey: 'hotel-me-cabo-by-melia',
categoryId: 'hotel',
name: 'ME Cabo by Meliá',
desc: 'Beach-club leaning stay for the group that wants energy and location over quiet luxury.',
details: ['Apple exact-date quote: $1,533 pp', 'Beach club energy', 'Medano Beach'],
links: [
{ label: 'ME Cabo', url: 'https://www.hotelmecabo.com/' },
],
}),
createOption({
id: 'hotel-paradisus-los-cabos',
seedKey: 'hotel-paradisus-los-cabos',
categoryId: 'hotel',
name: 'Paradisus Los Cabos',
desc: 'Upscale all-inclusive with strong amenities and a better balance than the ultra-luxe splurge properties.',
details: ['Apple exact-date quote: $1,722 pp', 'Spa-forward', 'Adults-friendly luxury'],
links: [
{ label: 'Paradisus', url: 'https://www.paradisusloscabosresort.com/' },
],
}),
createOption({
id: 'hotel-hard-rock-los-cabos',
seedKey: 'hotel-hard-rock-los-cabos',
categoryId: 'hotel',
name: 'Hard Rock Hotel Los Cabos',
desc: 'The loudest splurge option from todays Apple search, with the highest quoted price on the list.',
details: ['Apple exact-date quote: $3,343 pp', 'Premium splurge', 'High-energy all-inclusive'],
links: [
{ label: 'Hard Rock', url: 'https://hotel.hardrock.com/los-cabos' },
],
}),
createOption({
id: 'hotel-solmar-resort',
seedKey: 'hotel-solmar-resort',
categoryId: 'hotel',
name: 'Solmar Resort',
desc: 'Low-cost KAYAK option if the group wants to keep the room line item very lean.',
details: ['KAYAK exact-date quote: $185/night', 'Budget-friendly', 'Downtown-adjacent'],
links: [
{ label: 'Solmar', url: 'https://www.solmar.com/en/hotels/cabo-san-lucas/solmar-resort/' },
],
}),
createOption({
id: 'hotel-tesoro-los-cabos',
seedKey: 'hotel-tesoro-los-cabos',
categoryId: 'hotel',
name: 'Tesoro Los Cabos',
desc: 'Marina-side value stay that landed in the middle of the KAYAK result set today.',
details: ['KAYAK exact-date quote: $250/night', 'Marina access', 'Low-mid budget'],
links: [
{ label: 'Tesoro', url: 'https://tesoroloscabos.com/' },
],
}),
createOption({
id: 'hotel-grand-solmar-lands-end',
seedKey: 'hotel-grand-solmar-lands-end',
categoryId: 'hotel',
name: "Grand Solmar Land's End Resort & Spa",
desc: 'Luxury Pacific-side resort with a stronger price tag than the value stays but below the ultra-splurge properties.',
details: ['KAYAK exact-date quote: $712/night', 'Luxury', 'Pacific-side'],
links: [
{ label: 'Grand Solmar', url: 'https://grandsolmarresort.solmar.com/' },
],
}),
createOption({
id: 'hotel-comfort-inn-suites-los-cabos',
seedKey: 'hotel-comfort-inn-suites-los-cabos',
categoryId: 'hotel',
name: 'Comfort Inn & Suites Los Cabos',
desc: 'Bare-bones KAYAK option if the group wants a practical bed-and-shower stay.',
details: ['KAYAK exact-date quote: $129/night', 'Budget stay', 'Practical'],
links: [
{ label: 'KAYAK', url: 'https://www.kayak.com/Cabo-San-Lucas-Hotels-Comfort-Inn-Suites-Los-Cabos.1071781145.ksp' },
],
}),
createOption({
id: 'hotel-capital-o-hotel-dos-mares',
seedKey: 'hotel-capital-o-hotel-dos-mares',
categoryId: 'hotel',
name: 'Capital O Hotel Dos Mares, Cabo San Lucas',
desc: 'Lowest visible KAYAK price of the day, useful only if the group is aggressively minimizing room cost.',
details: ['KAYAK exact-date quote: $48/night', 'Lowest visible price', 'Budget'],
links: [
{ label: 'OYO', url: 'https://www.oyorooms.com/mx/92226/' },
],
}),
createOption({
id: 'hotel-villa-del-palmar-beach-resort-cabo-san-lucas',
seedKey: 'hotel-villa-del-palmar-beach-resort-cabo-san-lucas',
categoryId: 'hotel',
name: 'Villa del Palmar Beach Resort Cabo San Lucas',
desc: 'Broad-appeal beach resort with a middle-of-the-road KAYAK room price today.',
details: ['KAYAK exact-date quote: $460/night', 'Beach resort', 'Family-friendly'],
links: [
{ label: 'Villa del Palmar', url: 'https://cabo.villadelpalmar.com/' },
],
}),
createOption({
id: 'hotel-esperanza-auberge-collection',
seedKey: 'hotel-esperanza-auberge-collection',
categoryId: 'hotel',
name: 'Esperanza, Auberge Collection',
desc: 'Top-end KAYAK splurge result from today, priced well above the other options in the set.',
details: ['KAYAK exact-date quote: $3,243/night', 'Luxury splurge', 'Auberge Collection'],
links: [
{ label: 'Auberge', url: 'https://aubergeresorts.com/esperanza/' },
],
}),
createOption({
id: 'flight-ont-sjd-kayak-cheapest',
seedKey: 'flight-ont-sjd-kayak-cheapest',
categoryId: 'flight',
name: 'ONT to SJD on KAYAK - Cheapest',
desc: 'Lowest exact-date round-trip flight quote from Ontario to San Jose del Cabo for the target trip window.',
details: ['$402 RT pp', 'Volaris, 1 stop', 'Best budget-flight anchor'],
links: [
{ label: 'KAYAK Flight Search', url: 'https://www.kayak.com/flights/ONT-SJD/2027-02-02/2027-02-06?sort=bestflight_a' },
],
}),
createOption({
id: 'flight-ont-sjd-kayak-best',
seedKey: 'flight-ont-sjd-kayak-best',
categoryId: 'flight',
name: 'ONT to SJD on KAYAK - Best',
desc: 'Higher-comfort exact-date round-trip flight quote for the target trip window.',
details: ['$605 RT pp', 'American, 1 stop', 'Best-value comfort anchor'],
links: [
{ label: 'KAYAK Flight Search', url: 'https://www.kayak.com/flights/ONT-SJD/2027-02-02/2027-02-06?sort=bestflight_a' },
],
}),
createOption({
id: 'golf-palmilla',
seedKey: 'golf-palmilla',
categoryId: 'golf',
name: 'Palmilla Golf Club',
desc: 'Best public price signal I found with transparent inclusions: green fee, shared cart, practice facilities, and bottled water.',
lat: 23.0519,
lng: -109.7058,
details: ['From $130 pp', 'Shared cart included', 'Strong budget-track pick'],
links: [
{ label: 'Cabo Villas Golf', url: 'https://www.cabovillas.com/golf/palmilla' },
{ label: 'Visit Los Cabos Golf Guide', url: 'https://www.visitloscabos.travel/golf/' },
],
}),
createOption({
id: 'golf-cabo-del-sol',
seedKey: 'golf-cabo-del-sol',
categoryId: 'golf',
name: 'Cabo del Sol',
desc: 'The most natural golf pairing for Grand Fiesta Americana and the balanced-track itinerary.',
lat: 23.0569,
lng: -109.6962,
details: ['Use about $180 as current planning number', 'Best balanced-track fit', 'Ocean-desert layout'],
links: [
{ label: 'Official', url: 'https://cabodelsol.com/' },
{ label: 'Visit Los Cabos Golf Guide', url: 'https://www.visitloscabos.travel/golf/' },
],
}),
createOption({
id: 'golf-quivira',
seedKey: 'golf-quivira',
categoryId: 'golf',
name: 'Quivira Golf Club',
desc: 'Premium golf move for the splurge weekend. Access is easiest through the Pueblo Bonito / Pacifica side of the destination.',
lat: 23.0403,
lng: -109.7221,
details: ['Use about $250 for planning', 'Pairs with Pacifica', 'Signature ocean holes'],
links: [
{ label: 'Official', url: 'https://www.quiviraloscabos.com/golf/' },
{ label: 'Pacifica FAQ', url: 'https://www.pueblobonito.com/resorts/pacifica/faq' },
],
}),
createOption({
id: 'golf-puerto-los-cabos',
seedKey: 'golf-puerto-los-cabos',
categoryId: 'golf',
name: 'Puerto Los Cabos Golf',
desc: 'Most natural pairing with Secrets Puerto Los Cabos if the group picks the upscale San Jose side.',
lat: 23.0308,
lng: -109.6964,
details: ['Convenient from Secrets', 'Upscale east-cape feel', 'Good alternative to Quivira'],
links: [
{ label: 'Visit Los Cabos Golf Guide', url: 'https://www.visitloscabos.travel/golf/' },
{ label: 'Secrets Official', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/secrets/mexico/puerto-los-cabos-golf-spa-resort/' },
],
}),
createOption({
id: 'nightlife-cabo-bash',
seedKey: 'nightlife-cabo-bash',
categoryId: 'nightlife',
name: 'Cabo Bash VIP Nightlife',
desc: 'Most turnkey option if you want the weekend hosted instead of DIY. They also coordinate yachts, villas, and day clubs.',
lat: 23.0627,
lng: -109.6989,
details: ['Gold package for 16 guests: $1,700', 'Concierge coordination', 'Strongest bachelor specialist'],
links: [
{ label: 'Bachelor Parties', url: 'https://www.cabobash.com/bachelor.html' },
{ label: 'Nightlife', url: 'https://www.cabobash.com/nightlife.html' },
{ label: 'Day Clubs', url: 'https://www.cabobash.com/day-clubs.html' },
],
}),
createOption({
id: 'nightlife-cabo-agency',
seedKey: 'nightlife-cabo-agency',
categoryId: 'nightlife',
name: 'The Cabo Agency VIP Tables',
desc: 'Best if you want to book specific tables instead of a full concierge weekend.',
lat: 23.0692,
lng: -109.6993,
details: ['Cabo Wabo VIP table $155 with $100 credit', 'Booth $400 with $300 credit', 'A la carte nightlife'],
links: [
{ label: 'VIP Tables', url: 'https://www.thecaboagency.com/cabo_vip_tables.php' },
{ label: 'Entertainment Packages', url: 'https://www.thecaboagency.com/cabo_entertainment_packages.php' },
],
}),
createOption({
id: 'nightlife-taboo',
seedKey: 'nightlife-taboo',
categoryId: 'nightlife',
name: 'Taboo Beach Club',
desc: 'High-spend daytime flex. Better for a splashy afternoon than an all-weekend base.',
lat: 23.0637,
lng: -109.7001,
details: ['Pool island for 4: $884', 'Cabana for 8: $2,060', 'Use for splurge tier'],
links: [
{ label: 'Cabo Bash Day Clubs', url: 'https://www.cabobash.com/day-clubs.html' },
],
}),
createOption({
id: 'nightlife-mango-deck',
seedKey: 'nightlife-mango-deck',
categoryId: 'nightlife',
name: 'Mango Deck / Office Zone',
desc: 'Best lower-spend party zone if you want daytime chaos close to Medano Beach without burning the whole budget.',
lat: 23.0631,
lng: -109.6995,
details: ['Mango Deck deposit from $40 pp', 'Easy with Corazon', 'Budget-track friendly'],
links: [
{ label: 'Cabo Bash Day Clubs', url: 'https://www.cabobash.com/day-clubs.html' },
],
}),
createOption({
id: 'excursion-whale-public',
seedKey: 'excursion-whale-public',
categoryId: 'excursion',
name: 'Cabo Adventures Whale Watching',
desc: 'February is prime whale season, and this is the cleanest official public-tour price signal I found.',
lat: 23.0626,
lng: -109.7004,
details: ['From $76', 'Prime season in February', 'Dock fee and transport extras may apply'],
links: [
{ label: 'Official', url: 'https://www.cabo-adventures.com/en/' },
{ label: 'Whale Season Guide', url: 'https://www.visitloscabos.travel/blog/post/whale-watching-in-los-cabos-2025-the-ultimate-guide-to-an-unforgettable-season/' },
],
}),
createOption({
id: 'excursion-whale-private',
seedKey: 'excursion-whale-private',
categoryId: 'excursion',
name: 'Private Whale Watching Charter',
desc: 'Best splurge-group activity because the per-person hit gets much better as the group size rises.',
lat: 23.0626,
lng: -109.7004,
details: ['From $1,504 total', 'About $188 pp at 8', 'About $125 pp at 12'],
links: [
{ label: 'Cabo Adventures Private Tours', url: 'https://www.cabo-adventures.com/en/tours/private-cabo/' },
],
}),
createOption({
id: 'excursion-atv',
seedKey: 'excursion-atv',
categoryId: 'excursion',
name: 'ATV Desert Adventure',
desc: 'Classic bachelor-party activity with a real extra-fee caveat worth budgeting up front.',
lat: 23.0289,
lng: -109.6689,
details: ['About $78-$91', '$35 damage waiver per vehicle', 'Entrance fee noted at check-in'],
links: [
{ label: 'Tour Landing Page', url: 'https://www.cabo-adventures.com/en/tours/land-adventures/' },
{ label: 'ATV Details', url: 'https://www.cabo-adventures.com/en/tour/atv-desert-adventure/' },
],
}),
createOption({
id: 'excursion-sail',
seedKey: 'excursion-sail',
categoryId: 'excursion',
name: 'Sunset Sail / Public Yacht Day',
desc: 'Best balanced activity if the group wants a solid Cabo moment without chartering the entire day.',
lat: 23.0634,
lng: -109.6978,
details: ['Public sail from $109', 'Sunset sail $124.50', 'Private 38 foot sailboat from $855.94'],
links: [
{ label: 'Cabo Sailing', url: 'https://www.cabovillas.com/water-tours/cabo-sailing' },
],
}),
createOption({
id: 'itinerary-budget',
seedKey: 'itinerary-budget',
categoryId: 'itinerary',
name: 'Budget Track: Corazon + Palmilla + Public Activity',
desc: 'Best option if the group wants a real Cabo bachelor trip while keeping the all-in number close to the low-$1.4k range before extra bar tabs.',
lat: 23.0639,
lng: -109.6991,
details: ['8 guys: about $1,405 pp', '10 guys: about $1,398 pp', '12 guys: about $1,392 pp'],
links: [
{ label: 'Corazon', url: 'https://www.corazoncabo.com/' },
{ label: 'Palmilla', url: 'https://www.cabovillas.com/golf/palmilla' },
{ label: 'Transfers', url: 'https://www.cabovillas.com/transportation' },
],
}),
createOption({
id: 'itinerary-balanced',
seedKey: 'itinerary-balanced',
categoryId: 'itinerary',
name: 'Balanced Track: Grand Fiesta + Golf + Sail',
desc: 'Best all-around answer for a group that wants fewer logistics, a nice resort, and one clean golf day.',
lat: 23.0949,
lng: -109.7067,
details: ['8 guys: about $1,688 pp', '10 guys: about $1,681 pp', '12 guys: about $1,677 pp'],
links: [
{ label: 'Grand Fiesta', url: 'https://www.fiestamericanatravelty.com/en/grand-fiesta-americana/hotels/grand-fiesta-americana-los-cabos-all-inclusive-golf-and-spa' },
{ label: 'Costco Package', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSGRANDFIESTA20171011' },
{ label: 'Sailing', url: 'https://www.cabovillas.com/water-tours/cabo-sailing' },
],
}),
createOption({
id: 'itinerary-splurge',
seedKey: 'itinerary-splurge',
categoryId: 'itinerary',
name: 'Splurge Track: Breathless or Secrets + Premium Golf + VIP Night',
desc: 'Best if the weekend is really about going big once, with the budget climbing above $2.2k per person depending on group size.',
lat: 23.0628,
lng: -109.6981,
details: ['8 guys: about $2,484 pp', '10 guys: about $2,346 pp', '12 guys: about $2,289 pp'],
links: [
{ label: 'Breathless', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/breathless/mexico/cabo-san-lucas-resort-spa/' },
{ label: 'Secrets', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/secrets/mexico/puerto-los-cabos-golf-spa-resort/' },
{ label: 'VIP Tables', url: 'https://www.thecaboagency.com/cabo_vip_tables.php' },
],
}),
createOption({
id: 'itinerary-concierge',
seedKey: 'itinerary-concierge',
categoryId: 'itinerary',
name: 'Concierge Route: Cabo Bash / Cabo Agency',
desc: 'Best if the group wants to stop spreadsheeting and hand flights, villas, yachts, transfers, and nightlife to a specialist.',
lat: 23.0633,
lng: -109.6992,
details: ['Most turnkey', 'Great for split budgets', 'Request quote for final pricing'],
links: [
{ label: 'Cabo Bash', url: 'https://www.cabobash.com/bachelor.html' },
{ label: 'The Cabo Agency', url: 'https://www.thecaboagency.com/cabo_bachelor_party.php' },
{ label: 'Blue Desert Package', url: 'https://www.bluedesertcabo.com/activities/packages/bachelor-party-vacation-package/' },
],
}),
createOption({
id: 'budget-option-budget',
seedKey: 'budget-option-budget',
categoryId: 'budget',
name: 'Budget Track',
desc: 'Corazon + one golf round + one public activity + one nightlife night. Best value if you want the trip fun but not financially reckless.',
details: ['8: $1,405 pp', '10: $1,398 pp', '12: $1,392 pp'],
links: [
{ label: 'Corazon', url: 'https://www.corazoncabo.com/' },
{ label: 'Palmilla', url: 'https://www.cabovillas.com/golf/palmilla' },
],
}),
createOption({
id: 'budget-option-balanced',
seedKey: 'budget-option-balanced',
categoryId: 'budget',
name: 'Balanced Track',
desc: 'Grand Fiesta all-inclusive + better golf + sunset sail + one nightlife push. Strongest overall bachelor-weekend value.',
details: ['8: $1,688 pp', '10: $1,681 pp', '12: $1,677 pp'],
links: [
{ label: 'Grand Fiesta', url: 'https://www.fiestamericanatravelty.com/en/grand-fiesta-americana/hotels/grand-fiesta-americana-los-cabos-all-inclusive-golf-and-spa' },
{ label: 'Costco Travel', url: 'https://www.costcotravel.com/Vacation-Packages/Offers/MEXLOSCABOSGRANDFIESTA20171011' },
],
}),
createOption({
id: 'budget-option-splurge',
seedKey: 'budget-option-splurge',
categoryId: 'budget',
name: 'Splurge Track',
desc: 'Adults-only resort, premium golf, private charter, and VIP nightlife. This is the blowout version.',
details: ['8: $2,484 pp', '10: $2,346 pp', '12: $2,289 pp'],
links: [
{ label: 'Breathless', url: 'https://www.hyattinclusivecollection.com/en/resorts-hotels/breathless/mexico/cabo-san-lucas-resort-spa/' },
{ label: 'Private Tour', url: 'https://www.cabo-adventures.com/en/tours/private-cabo/' },
],
}),
],
voters: [],
pollsOpen: true,
};
}
function mergeSeedData(existing = {}) {
const seed = buildSeedData();
const existingOptions = Array.isArray(existing.options) ? existing.options : [];
const mergedSeedOptions = seed.options.map((seedOption) => {
const match = existingOptions.find((option) => (
option.seedKey === seedOption.seedKey
|| (option.addedBy === 'system' && option.categoryId === seedOption.categoryId && option.name === seedOption.name)
));
return {
...seedOption,
votes: Array.isArray(match?.votes) ? match.votes : [],
approved: typeof match?.approved === 'boolean' ? match.approved : seedOption.approved,
addedBy: match?.addedBy || seedOption.addedBy,
};
});
const preservedCustomOptions = existingOptions.filter((option) => {
if (option.addedBy === 'system') return false;
return !mergedSeedOptions.some((seedOption) => (
(seedOption.seedKey && option.seedKey === seedOption.seedKey)
|| (seedOption.categoryId === option.categoryId && seedOption.name === option.name)
));
});
const existingCategories = Array.isArray(existing.categories) ? existing.categories : [];
const preservedCustomCategories = existingCategories.filter(
(category) => !seed.categories.some((seedCategory) => seedCategory.id === category.id),
);
return {
...existing,
seedVersion: seed.seedVersion,
priceUpdatedAt: seed.priceUpdatedAt,
categories: [...seed.categories, ...preservedCustomCategories],
budgetScenarios: seed.budgetScenarios,
guestRoster: seed.guestRoster,
options: [...mergedSeedOptions, ...preservedCustomOptions],
voters: Array.isArray(existing.voters) ? existing.voters : [],
pollsOpen: typeof existing.pollsOpen === 'boolean' ? existing.pollsOpen : true,
};
}
module.exports = {
SEED_VERSION,
PRICE_UPDATED_AT,
CATEGORY_META,
buildSeedData,
mergeSeedData,
};