diff --git a/src/app/demo/page.tsx b/src/app/demo/page.tsx new file mode 100644 index 0000000..29d314c --- /dev/null +++ b/src/app/demo/page.tsx @@ -0,0 +1,632 @@ +import Link from 'next/link' +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'rTrips Demo - Alpine Explorer 2026', + description: 'See how rTrips and the rStack ecosystem power collaborative trip planning. A demo showcasing rMaps, rNotes, rCal, rVote, rFunds, and rCart working together.', +} + +/* ─── Mock Data ─────────────────────────────────────────────── */ + +const members = [ + { name: 'Alex', color: 'bg-teal-500' }, + { name: 'Sam', color: 'bg-cyan-500' }, + { name: 'Jordan', color: 'bg-blue-500' }, + { name: 'Riley', color: 'bg-violet-500' }, + { name: 'Casey', color: 'bg-amber-500' }, + { name: 'Morgan', color: 'bg-rose-500' }, +] + +const packingList = [ + { item: 'Hiking boots (broken in!)', checked: true }, + { item: 'Rain jacket & layers', checked: true }, + { item: 'Headlamp + spare batteries', checked: true }, + { item: 'First aid kit', checked: false }, + { item: 'Sunscreen SPF 50', checked: true }, + { item: 'Trekking poles', checked: false }, + { item: 'Refillable water bottle', checked: true }, + { item: 'Via ferrata gloves', checked: false }, +] + +const tripRules = [ + 'Majority vote on daily activities', + 'Shared expenses split equally', + 'Quiet hours after 10pm in huts', + 'Everyone carries their own pack', +] + +const calendarEvents: Record = { + 6: [{ label: 'Fly to Geneva', color: 'bg-teal-500' }], + 7: [{ label: 'Chamonix check-in', color: 'bg-teal-500' }], + 8: [{ label: 'Mont Blanc hike', color: 'bg-emerald-500' }], + 9: [{ label: 'Rest / explore town', color: 'bg-slate-500' }], + 10: [{ label: 'Mer de Glace trek', color: 'bg-emerald-500' }], + 11: [{ label: 'Via ferrata', color: 'bg-amber-500' }], + 12: [{ label: 'Train to Zermatt', color: 'bg-cyan-500' }], + 13: [{ label: 'Matterhorn viewpoint', color: 'bg-emerald-500' }], + 14: [{ label: 'Mountain biking', color: 'bg-violet-500' }], + 15: [{ label: 'Gorner Gorge hike', color: 'bg-emerald-500' }], + 16: [{ label: 'Paragliding!', color: 'bg-rose-500' }], + 17: [{ label: 'Travel to Dolomites', color: 'bg-cyan-500' }], + 18: [{ label: 'Tre Cime circuit', color: 'bg-emerald-500' }], + 19: [{ label: 'Lake Braies kayaking', color: 'bg-blue-500' }], + 20: [{ label: 'Fly home', color: 'bg-teal-500' }], +} + +const polls = [ + { + question: 'Day 5 Activity?', + options: [ + { label: 'Via Ferrata', votes: 4, color: 'bg-amber-500' }, + { label: 'Kayaking', votes: 1, color: 'bg-blue-500' }, + { label: 'Rest day', votes: 1, color: 'bg-slate-500' }, + ], + totalVotes: 6, + }, + { + question: 'Dinner tonight?', + options: [ + { label: 'Fondue place', votes: 3, color: 'bg-amber-500' }, + { label: 'Pizza by the lake', votes: 2, color: 'bg-rose-500' }, + { label: 'Cook at Airbnb', votes: 1, color: 'bg-emerald-500' }, + ], + totalVotes: 6, + }, +] + +const expenses = [ + { desc: 'Geneva → Chamonix shuttle', who: 'Alex', amount: 186, split: 6 }, + { desc: 'Mountain hut (2 nights)', who: 'Sam', amount: 420, split: 6 }, + { desc: 'Via ferrata gear rental', who: 'Jordan', amount: 144, split: 4 }, + { desc: 'Groceries (Zermatt)', who: 'Casey', amount: 93, split: 6 }, + { desc: 'Paragliding deposit', who: 'Riley', amount: 360, split: 3 }, +] + +const cartItems = [ + { item: 'Group first-aid kit', target: 85, funded: 85, status: 'Purchased' as const }, + { item: 'Portable water filter', target: 45, funded: 45, status: 'Purchased' as const }, + { item: 'Bear canister (2x)', target: 120, funded: 90, status: 'Funding' as const }, + { item: 'Camp stove + fuel', target: 65, funded: 65, status: 'Purchased' as const }, + { item: 'Drone (group footage)', target: 350, funded: 210, status: 'Funding' as const }, + { item: 'Starlink Mini rental', target: 200, funded: 80, status: 'Funding' as const }, +] + +/* ─── Card Wrapper ──────────────────────────────────────────── */ + +function CardWrapper({ + icon, + title, + service, + href, + span = 1, + children, +}: { + icon: string + title: string + service: string + href: string + span?: 1 | 2 + children: React.ReactNode +}) { + return ( +
+
+
+ {icon} + {title} +
+ + Open in {service} ↗ + +
+
{children}
+
+ ) +} + +/* ─── Card: rMaps ───────────────────────────────────────────── */ + +function RMapsCard() { + return ( + +
+ + {/* Mountain silhouettes */} + + + {/* Snow caps */} + + + + + {/* Route line */} + + + {/* Destination pins */} + {/* Chamonix */} + + + Chamonix + + + Jul 6–11 + + + {/* Zermatt */} + + + Zermatt + + + Jul 12–16 + + + {/* Dolomites */} + + + Dolomites + + + Jul 17–20 + + + {/* Activity icons along route */} + 🥾 + 🧗 + 🚵 + 🪂 + 🛶 + + + {/* Legend */} +
+ + France + + + Switzerland + + + Italy + +
+
+
+ ) +} + +/* ─── Card: rNotes ──────────────────────────────────────────── */ + +function RNotesCard() { + return ( + +
+ {/* Packing list */} +
+

+ Packing Checklist +

+
    + {packingList.map((item) => ( +
  • + + {item.checked && ( + + + + )} + + + {item.item} + +
  • + ))} +
+
+ + {/* Trip rules */} +
+

+ Trip Rules +

+
    + {tripRules.map((rule) => ( +
  1. {rule}
  2. + ))} +
+
+
+
+ ) +} + +/* ─── Card: rCal ────────────────────────────────────────────── */ + +function RCalCard() { + const daysInJuly = 31 + const startDay = 0 // July 2026 starts on Wednesday (0=Mon grid: Wed=2, but let's use simple offset) + // July 1, 2026 is a Wednesday. In a Mon-start grid, offset = 2 + const offset = 2 + const days = Array.from({ length: daysInJuly }, (_, i) => i + 1) + const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + + return ( + +
+
+

July 2026

+ 15 days • 3 countries +
+ + {/* Day headers */} +
+ {dayNames.map((d) => ( +
+ {d} +
+ ))} +
+ + {/* Calendar grid */} +
+ {/* Empty offset cells */} + {Array.from({ length: offset }).map((_, i) => ( +
+ ))} + + {days.map((day) => { + const events = calendarEvents[day] + const isTrip = day >= 6 && day <= 20 + return ( +
+ + {day} + + {events?.map((e) => ( +
+ {e.label} +
+ ))} +
+ ) + })} +
+ + {/* Activity legend */} +
+ Travel + Hiking + Adventure + Biking + Extreme + Water + Transit +
+
+ + ) +} + +/* ─── Card: rVote ───────────────────────────────────────────── */ + +function RVoteCard() { + return ( + +
+ {polls.map((poll) => ( +
+

{poll.question}

+
+ {poll.options.map((opt) => { + const pct = Math.round((opt.votes / poll.totalVotes) * 100) + return ( +
+
+ {opt.label} + + {opt.votes} vote{opt.votes !== 1 ? 's' : ''} ({pct}%) + +
+
+
+
+
+ ) + })} +
+

{poll.totalVotes} votes cast

+
+ ))} +
+ + ) +} + +/* ─── Card: rFunds ──────────────────────────────────────────── */ + +function RFundsCard() { + const totalSpent = expenses.reduce((s, e) => s + e.amount, 0) + + // Calculate balances per member + const balances: Record = {} + members.forEach((m) => (balances[m.name] = 0)) + expenses.forEach((e) => { + const share = e.amount / e.split + balances[e.who] = (balances[e.who] || 0) + e.amount // they paid + // Everyone who splits owes their share + members.slice(0, e.split).forEach((m) => { + balances[m.name] = (balances[m.name] || 0) - share + }) + }) + + return ( + +
+ {/* Total */} +
+

€{totalSpent.toLocaleString()}

+

Total group spending

+
+ + {/* Recent transactions */} +
+

+ Recent +

+
+ {expenses.slice(0, 4).map((e) => ( +
+
+

{e.desc}

+

+ {e.who} • split {e.split} ways +

+
+ €{e.amount} +
+ ))} +
+
+ + {/* Balances */} +
+

+ Balances +

+
+ {members.map((m) => { + const bal = balances[m.name] || 0 + return ( +
+ {m.name} + = 0 ? 'text-emerald-400' : 'text-rose-400'}> + {bal >= 0 ? '+' : ''}€{Math.round(bal)} + +
+ ) + })} +
+
+
+
+ ) +} + +/* ─── Card: rCart ────────────────────────────────────────────── */ + +function RCartCard() { + const totalFunded = cartItems.reduce((s, i) => s + i.funded, 0) + const totalTarget = cartItems.reduce((s, i) => s + i.target, 0) + + return ( + +
+ {/* Summary bar */} +
+ + €{totalFunded} / €{totalTarget} funded + + + {cartItems.filter((i) => i.status === 'Purchased').length}/{cartItems.length} purchased + +
+
+
+
+ + {/* Items grid */} +
+ {cartItems.map((item) => { + const pct = Math.round((item.funded / item.target) * 100) + return ( +
+
+ {item.item} + {item.status === 'Purchased' ? ( + + ✓ Bought + + ) : ( + €{item.funded}/€{item.target} + )} +
+
+
+
+
+ ) + })} +
+
+ + ) +} + +/* ─── Page ──────────────────────────────────────────────────── */ + +export default function DemoPage() { + return ( +
+ {/* Nav */} + + + {/* Trip Header */} +
+
+

+ Alpine Explorer 2026 +

+

+ Chamonix → Zermatt → Dolomites +

+
+ 📅 Jul 6–20, 2026 + 💶 ~€4,500 budget + 🏔️ 3 countries +
+ + {/* Member avatars */} +
+ {members.map((m) => ( +
+ {m.name[0]} +
+ ))} + 6 explorers +
+
+
+ + {/* rStack Intro */} +
+

+ Every trip is powered by the rStack — a suite + of collaborative tools that handle routes, notes, schedules, voting, expenses, and shared + purchases. Each card below is a live preview with a link to the full tool. +

+
+ + {/* Canvas Grid */} +
+
+ + + + + + +
+
+ + {/* Bottom CTA */} +
+
+

Plan Your Own Group Adventure

+

+ The rStack gives your group everything you need — routes, schedules, polls, shared + expenses, and gear lists — all connected in one trip canvas. +

+ + Start Planning + +
+
+ + {/* Footer */} + +
+ ) +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 4bb7f79..232f02c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,6 +13,12 @@ export default function Home() { rTrips
+ + Demo +