A Palantir-ish dark command-center dashboard for family trip planning with convoy routes, timeline playback, meal logistics, and Google Maps integration.
Skill by ara.so — Daily 2026 Skills collection.
A deliberately overbuilt, dark-themed operations dashboard for coordinating multi-family road trips. Built with React 19, Vite, Google Maps JS API, Framer Motion, and Lucide icons. Treats a weekend cabin trip like a live ops room: convoy tracking, arrival windows, day-by-day timeline playback, meal logistics, expense splits, and family checklists.
git clone https://github.com/andrewjiang/palantir-for-family-trips.git
cd palantir-for-family-trips
npm install
cp .env.example .env
Edit :
.envVITE_GOOGLE_MAPS_API_KEY=your_browser_maps_key_here
VITE_GOOGLE_MAP_ID=your_optional_google_map_id
npm run dev
Open http://127.0.0.1:5173 (or the URL Vite prints).
Without a Maps API key the UI fully renders but the live Google Map layer won't initialize. All other views work without it.
src/
App.jsx # Main shell, tabs, timeline controls, overlays
CommandMap.jsx # Google Maps route rendering, convoy playback
tripModel.js # Seed trip document, families, routes, helpers
main.jsx # React entry point
index.css # Global dark-theme styles
public/
docs/ # Screenshot assets for README
.env.example
src/tripModel.js)The trip is a single JS object (the "trip document") exported from tripModel.js. Everything — families, routes, days, meals, activities, expenses — lives here.
// src/tripModel.js (simplified)
export const trip = {
name: "Pine Mountain Lake & Yosemite 2026",
basecamp: {
label: "Pine Mountain Lake Cabin",
coords: { lat: 37.85, lng: -120.17 },
},
families: [
{
id: "fam-a",
name: "The Johnsons",
origin: { label: "San Francisco, CA", coords: { lat: 37.77, lng: -122.41 } },
members: ["Alice", "Bob", "Charlie (8)", "Dana (5)"],
vehicle: "Blue Minivan",
arrivalWindow: { earliest: "2026-06-27T14:00:00", latest: "2026-06-27T16:00:00" },
color: "#4ade80",
},
{
id: "fam-b",
name: "The Garcias",
origin: { label: "San Jose, CA", coords: { lat: 37.33, lng: -121.88 } },
members: ["Elena", "Marco", "Sofia (6)"],
vehicle: "Silver SUV",
arrivalWindow: { earliest: "2026-06-27T15:30:00", latest: "2026-06-27T17:00:00" },
color: "#f97316",
},
],
days: [
{
date: "2026-06-27",
label: "Arrival Day",
events: [
{ time: "14:00", type: "arrival", familyId: "fam-a", note: "Johnsons ETA" },
{ time: "18:30", type: "meal", mealId: "dinner-fri", note: "Group dinner at basecamp" },
],
},
// …more days
],
meals: [
{
id: "dinner-fri",
label: "Friday Dinner",
type: "dinner",
location: "basecamp",
assignedTo: "fam-a",
menu: ["Tacos", "Chips & Guac", "Watermelon"],
shoppingList: ["tortillas", "ground beef", "salsa"],
},
],
activities: [
{
id: "act-hike-1",
label: "Mariposa Grove Hike",
day: "2026-06-28",
time: "09:00",
durationHours: 3,
location: { label: "Mariposa Grove, Yosemite", coords: { lat: 37.5, lng: -119.6 } },
suitableAges: "5+",
notes: "Bring water and snacks",
},
],
expenses: [
{ id: "exp-1", label: "Cabin rental", amount: 900, paidBy: "fam-a", splitAmong: ["fam-a", "fam-b"] },
],
checklist: {
"fam-a": ["Pack hiking boots", "Download offline maps", "Bring beach towels"],
"fam-b": ["Bring board games", "Confirm car seats"],
},
};
// Helper: get a family by id
export function getFamily(id) {
return trip.families.find((f) => f.id === id);
}
// Helper: get events for a given date string "YYYY-MM-DD"
export function getDayEvents(dateStr) {
const day = trip.days.find((d) => d.date === dateStr);
return day ? day.events : [];
}
// Helper: total expense owed per family
export function splitExpense(expense) {
const share = expense.amount / expense.splitAmong.length;
return expense.splitAmong.map((famId) => ({ famId, owes: share }));
}
Edit src/tripModel.js: