203 lines
5.1 KiB
Markdown
203 lines
5.1 KiB
Markdown
# 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 metadata
|
|
- `loading`: Loading state for async operations
|
|
- `activeDayId`: Currently selected day
|
|
|
|
**Derived State** (computed automatically):
|
|
- `activeDay`: Current day object
|
|
- `activeDayPlaces`: Places for the active day
|
|
- `allMapMarkers`: All markers across all days
|
|
- `activeDayMapMarkers`: Markers for active day only
|
|
- `activeDayTimelineBlocks`: Timeline blocks grouped by time of day
|
|
|
|
**UI State**:
|
|
- `hoveredPlaceId`: Place being hovered
|
|
- `selectedPlaceId`: Place being selected
|
|
- `highlightedPlaceId`: Place being highlighted (animation)
|
|
- `newlyAddedPlaceId`: Newly added place (for scroll/highlight)
|
|
|
|
**Actions**:
|
|
- `loadTrip(tripId)`: Load trip from database
|
|
- `addPlaceToDay(placeId, dayId)`: Add place to a day
|
|
- `removePlaceFromDay(tripPlaceId)`: Remove place from day
|
|
- `reorderPlaces(dayId, places)`: Reorder places via drag & drop
|
|
- `updateTripData(updates)`: Update trip metadata
|
|
- `setActiveDayId(dayId)`: Change active day
|
|
- UI state setters for hover/select/highlight
|
|
|
|
### 2. Refactored TripPlanner.tsx
|
|
|
|
**Removed**:
|
|
- Local `trip` state
|
|
- Local `loading` state
|
|
- Local `activeDayId` state
|
|
- Duplicate `hoveredPlaceId`, `selectedPlaceId`, etc.
|
|
- Local `loadTrip()` function
|
|
- Computed `allPlaces` and `activeDayPlaces` (now from context)
|
|
|
|
**Now Uses Context**:
|
|
```typescript
|
|
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**:
|
|
```typescript
|
|
<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**:
|
|
```typescript
|
|
function MyComponent({ trip, activeDayId, places, onUpdate }) {
|
|
// Use props
|
|
}
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
function MyComponent() {
|
|
const { trip, activeDayId, activeDayPlaces, updateTripData } = useTrip();
|
|
// Use context directly
|
|
}
|
|
```
|
|
|
|
### For Components Updating Trip Data:
|
|
|
|
**Before**:
|
|
```typescript
|
|
await tripPlacesApi.remove(id);
|
|
await loadTrip(); // Manual reload
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
await removePlaceFromDay(id); // Automatic reload
|
|
```
|
|
|
|
## Future Improvements
|
|
|
|
1. **Add Optimistic Updates**: Update UI immediately, rollback on error
|
|
2. **Add Undo/Redo**: Track state history in context
|
|
3. **Add Caching**: Cache trip data to reduce API calls
|
|
4. **Add Real-time Sync**: Use Supabase Realtime for multi-user editing
|
|
5. **Split Context**: Separate UI state from data state if needed
|
|
|
|
## Testing Recommendations
|
|
|
|
1. Test TripContext in isolation
|
|
2. Mock TripContext for component tests
|
|
3. Test derived values computation
|
|
4. Test action side effects
|
|
5. 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
|