5.1 KiB
5.1 KiB
Trip Planner Refactoring Summary
Overview
Refactored the trip planner system to use a single source of truth for all trip data, eliminating duplicate state management and improving data flow consistency.
Key Changes
1. Created Unified TripContext (src/contexts/TripContext.tsx)
Purpose: Centralized state management for all trip-related data
Core State:
trip: Main trip data including days, places, and metadataloading: Loading state for async operationsactiveDayId: Currently selected day
Derived State (computed automatically):
activeDay: Current day objectactiveDayPlaces: Places for the active dayallMapMarkers: All markers across all daysactiveDayMapMarkers: Markers for active day onlyactiveDayTimelineBlocks: Timeline blocks grouped by time of day
UI State:
hoveredPlaceId: Place being hoveredselectedPlaceId: Place being selectedhighlightedPlaceId: Place being highlighted (animation)newlyAddedPlaceId: Newly added place (for scroll/highlight)
Actions:
loadTrip(tripId): Load trip from databaseaddPlaceToDay(placeId, dayId): Add place to a dayremovePlaceFromDay(tripPlaceId): Remove place from dayreorderPlaces(dayId, places): Reorder places via drag & dropupdateTripData(updates): Update trip metadatasetActiveDayId(dayId): Change active day- UI state setters for hover/select/highlight
2. Refactored TripPlanner.tsx
Removed:
- Local
tripstate - Local
loadingstate - Local
activeDayIdstate - Duplicate
hoveredPlaceId,selectedPlaceId, etc. - Local
loadTrip()function - Computed
allPlacesandactiveDayPlaces(now from context)
Now Uses Context:
const {
trip,
loading,
activeDayId,
setActiveDayId,
activeDay,
activeDayPlaces,
allMapMarkers,
activeDayMapMarkers,
hoveredPlaceId,
setHoveredPlaceId,
// ... etc
loadTrip,
removePlaceFromDay,
reorderPlaces,
} = useTrip();
Benefits:
- Single source of truth for trip data
- Automatic re-renders when data changes
- No manual state synchronization needed
- Cleaner component code
- Easier to test and debug
3. Updated API Layer
Added to src/db/api.ts:
tripPlacesApi.updateOrderIndex(): Update single place order
4. Updated App.tsx
Added TripProvider:
<AuthProvider>
<CurrentTripProvider>
<TripProvider>
<Router>
{/* ... */}
</Router>
</TripProvider>
</CurrentTripProvider>
</AuthProvider>
Data Flow
Before Refactoring:
Database → TripPlanner (local state) → Props → Child Components
↓
Manual sync between:
- trip state
- activeDayId
- places arrays
- map markers
- timeline blocks
After Refactoring:
Database → TripContext (single source) → useTrip() hook → All Components
↓
Automatic derivation:
- activeDay from activeDayId
- activeDayPlaces from activeDay
- allMapMarkers from trip.days
- activeDayMapMarkers from activeDayId
- activeDayTimelineBlocks from activeDayPlaces
Benefits
1. Single Source of Truth
- All trip data lives in TripContext
- No duplicate state across components
- Consistent data everywhere
2. Automatic Derivation
- Derived values (markers, timeline blocks) computed automatically
- No manual synchronization needed
- Always consistent with source data
3. Simplified Components
- Components use
useTrip()hook - No prop drilling
- Cleaner, more maintainable code
4. Better Performance
- Memoized derived values
- Re-renders only when necessary
- Optimized data flow
5. Easier Testing
- Context can be mocked easily
- Isolated state management
- Clear data dependencies
Migration Guide
For Components Using Trip Data:
Before:
function MyComponent({ trip, activeDayId, places, onUpdate }) {
// Use props
}
After:
function MyComponent() {
const { trip, activeDayId, activeDayPlaces, updateTripData } = useTrip();
// Use context directly
}
For Components Updating Trip Data:
Before:
await tripPlacesApi.remove(id);
await loadTrip(); // Manual reload
After:
await removePlaceFromDay(id); // Automatic reload
Future Improvements
- Add Optimistic Updates: Update UI immediately, rollback on error
- Add Undo/Redo: Track state history in context
- Add Caching: Cache trip data to reduce API calls
- Add Real-time Sync: Use Supabase Realtime for multi-user editing
- Split Context: Separate UI state from data state if needed
Testing Recommendations
- Test TripContext in isolation
- Mock TripContext for component tests
- Test derived values computation
- Test action side effects
- Test error handling in actions
Notes
- The Trip interface in TripContext includes all fields from the database schema
- Backward compatibility maintained with alias fields (start_date/startDate)
- Type casting used where necessary for component compatibility
- All lint errors resolved