Design: Trip Management (CRUD + List/Detail UI)¶
Date: 2025-11-17 Author: Jake Celentano (via Claude Code) Status: Proposal Project: trip-planner
Problem Statement¶
The trip-planner app currently has no way for users to create or manage trips. The AI chat interface exists but has no trip context, meaning: - Chat messages aren't associated with specific trips - Users can't save/load trip planning sessions - No persistence of trip metadata (dates, budget, constraints) - Map has no trip locations to display
This makes the app non-functional for its core purpose.
Goals¶
- [ ] Users can create new trips with basic metadata
- [ ] Users can view a list of their trips
- [ ] Users can select a trip to plan/view
- [ ] Chat interface receives tripId context
- [ ] Trip data persists in Supabase
Non-Goals¶
- Advanced trip collaboration/sharing (future)
- Trip templates or AI-generated trips (future)
- Complex budget tracking (separate feature)
- Calendar integration (future)
Proposed Solution¶
High-Level Architecture¶
┌─────────────────┐
│ Homepage │
│ (Trip List) │
└────────┬────────┘
│
├──> Create Trip Form (Dialog/Modal)
│
└──> Trip Detail Page
├─ Chat Interface (with tripId)
├─ Map (with trip locations)
└─ Trip Metadata (editable)
Components¶
1. Trip List Component (/app/page.tsx or /app/trips/page.tsx)
- Responsibility: Display user's trips, create new trip button
- Tech: Server Component (fetches trips from Supabase)
- Dependencies: Supabase server client
2. Create Trip Dialog (components/trips/CreateTripDialog.tsx)
- Responsibility: Form to create new trip
- Tech: Client Component with form state
- Dependencies: None (calls API route)
3. Trip Detail Page (/app/trips/[id]/page.tsx)
- Responsibility: Show trip with chat/map
- Tech: Server Component (fetches trip data)
- Dependencies: ChatInterface, TripMap components
4. API Route (/app/api/trips/route.ts)
- Responsibility: CRUD operations for trips
- Tech: Next.js API route with Supabase
- Dependencies: Supabase server client, auth
Data Model¶
Existing trips table (already created in migration):
CREATE TABLE trips (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
title TEXT NOT NULL,
description TEXT,
start_date DATE,
end_date DATE,
budget_amount DECIMAL(10, 2),
budget_currency TEXT DEFAULT 'USD',
status TEXT DEFAULT 'planning',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
No schema changes needed - table already exists from initial migration.
API Design¶
// POST /api/trips - Create trip
{
title: string
description?: string
start_date?: string // ISO date
end_date?: string
budget_amount?: number
budget_currency?: string
}
// GET /api/trips - List user's trips
Response: Trip[]
// GET /api/trips/[id] - Get single trip (optional, can use Server Component)
Response: Trip
// PATCH /api/trips/[id] - Update trip
{
title?: string
description?: string
// ... other fields
}
// DELETE /api/trips/[id] - Delete trip
Response: { success: boolean }
Alternatives Considered¶
Option 1: Everything on Homepage (Chat + List)¶
Pros: - Single page, no navigation - Simple for users
Cons: - Cluttered UI - Hard to focus on one trip - No deep linking
Verdict: Rejected - too cramped
Option 2: Separate Routes (/trips and /trips/[id]) ← Chosen¶
Pros: - Clean separation of concerns - Deep linkable URLs - Can expand each page independently - Follows Next.js conventions
Cons: - Requires navigation/routing
Verdict: Accepted - better UX and maintainability
Option 3: Modal-based (all on one page with dialogs)¶
Pros: - No page transitions - Modern SPA feel
Cons: - Complex state management - Hard to bookmark specific trip - Accessibility concerns
Verdict: Rejected - over-engineered for MVP
Implementation Plan¶
Phase 1: API Layer (1.5h)¶
- [ ] Create
/app/api/trips/route.ts(30 min) - GET (list trips)
- POST (create trip)
- [ ] Create
/app/api/trips/[id]/route.ts(30 min) - GET (single trip - optional)
- PATCH (update trip)
- DELETE (delete trip)
- [ ] Test with curl/Postman (30 min)
Phase 2: Trip List UI (1.5h)¶
- [ ] Create
/app/trips/page.tsx(45 min) - Fetch trips from API
- Display as cards/list
- Create trip button
- [ ] Create
components/trips/TripCard.tsx(30 min) - Display trip metadata
- Click → navigate to detail
- [ ] Create
components/trips/CreateTripDialog.tsx(15 min) - Form with title, dates, budget
- Submit → POST to API
Phase 3: Trip Detail Page (1.5h)¶
- [ ] Create
/app/trips/[id]/page.tsx(30 min) - Fetch trip data
- Render ChatInterface with tripId
- Render TripMap with trip locations (empty for now)
- [ ] Update
ChatInterfaceto accept tripId prop (15 min) - Pass to
/api/chatbody - [ ] Create trip metadata display (30 min)
- Editable fields (stretch goal)
- [ ] Navigation between list and detail (15 min)
Phase 4: Integration & Polish (30 min)¶
- [ ] Update homepage to redirect to /trips (5 min)
- [ ] Add loading states (10 min)
- [ ] Error handling (10 min)
- [ ] Basic styling polish (5 min)
Total estimate: ~5 hours
Risks & Mitigations¶
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| RLS policies block queries | High | Medium | Test auth flow thoroughly, verify user_id |
| Date validation issues | Medium | Low | Use HTML5 date inputs, validate on backend |
| No auth = can't test | High | Low | Use placeholder user_id for now, add auth later |
Testing Strategy¶
Manual testing (current standard): - Create trip with valid data → verify in Supabase - Create trip with missing required fields → verify error - List trips → verify user sees only their trips - Update trip → verify changes persist - Delete trip → verify trip removed - Select trip → verify chat receives tripId
Future (when testing added): - Unit tests for API routes - Component tests for forms
Observability¶
Not critical for MVP - no custom metrics needed yet.
Monitor: - Check Supabase logs for query errors - Check Next.js console for API errors
Rollout Plan¶
- Implement in LXC dev environment
- Test manually with real user flows
- Commit and push to GitHub
- (Optional) Deploy to k8s production
No production deployment required yet - dev-only feature for now.
Success Metrics¶
- [ ] Can create a trip with title and dates
- [ ] Trip appears in list
- [ ] Can navigate to trip detail page
- [ ] Chat interface shows trip context
- [ ] Changes persist across page refreshes
Open Questions¶
- [ ] Do we need auth now or use placeholder user_id? → Use placeholder for now
- [ ] Should trips be deletable or just archived? → Allow delete for MVP
- [ ] Do we show empty state when no trips exist? → Yes, with "Create your first trip" CTA
Status: Ready for implementation Next Step: Phase 1 - API Layer