38980-vm/app-9w9pd00g5j41/ITINERARY_FIX_SUMMARY.md
2026-03-04 18:25:09 +00:00

5.2 KiB

Itinerary Auto-Generation Fix Summary

Problems Fixed

1. Daily Place Count Issue

Problem: Timeline showed only 1-2 places per day instead of 3-5.

Root Cause: Faulty targetPlaces calculation:

// OLD (WRONG)
const targetPlaces = Math.min(
  MAX_PLACES_PER_DAY - dayPlaces.length,
  Math.max(MIN_PLACES_PER_DAY - dayPlaces.length, 0)
);

When balloon existed (dayPlaces.length = 1):

  • Result: Math.min(4, 1) = 1
  • Only 1 additional place added → Total 2 places

Fix: Simplified to fill all remaining slots:

// NEW (CORRECT)
const remainingSlots = MAX_PLACES_PER_DAY - dayPlaces.length;
// Add places until remainingSlots is filled or candidates run out

Now adds up to 5 places total per day


2. Order Index Collision

Problem: order_index conflicts when balloon place exists.

Root Cause: Loop index i didn't account for balloon at position 0.

Fix: Explicit order_index calculation:

let orderIndex: number;
if (isBalloon) {
  orderIndex = 0;  // Balloon always first
} else {
  const hasBalloon = dayPlaces.some(p => p.type === BALLOON_PLACE_TYPE);
  orderIndex = hasBalloon ? i : i;  // Sequential after balloon
}

Result:

  • With balloon: 0 (balloon), 1, 2, 3, 4
  • Without balloon: 0, 1, 2, 3, 4

3. Validation Logic Too Aggressive

Problem: isValidForDay blocked ALL same types in same day.

Old Logic:

// Blocked if type was used ANYWHERE in the day
if (place.type && usedTypesInDay.has(place.type)) {
  return false;
}

New Logic:

// Only block if CONSECUTIVE (last place was same type)
if (place.type && lastPlaceType === place.type) {
  return false;
}

Now allows: Museum → Viewpoint → Museum
Still blocks: Museum → Museum


4. Minimum Places Requirement

Problem: MIN_PLACES_PER_DAY was 2, but requirement is 3-5.

Fix: Updated cappadocia-rules.ts:

export const DAY_RULES: DayRules = {
  max_places: 5,
  min_places: 3,  // Changed from 2 to 3
  // ...
};

5. Duration Handling

Problem: Duration stored as string ("2 hours") but database expects integer minutes.

Fix: Safe parsing with defaults:

let durationMinutes = 120; // Default 2 hours

if (place.duration) {
  if (typeof place.duration === 'number') {
    durationMinutes = place.duration;
  } else if (typeof place.duration === 'string') {
    const match = place.duration.match(/(\d+)/);
    if (match) {
      durationMinutes = parseInt(match[1]) * 60; // Convert hours to minutes
    }
  }
} else {
  // Use typical duration from rules
  const typicalDuration = getTypicalDuration(place.type || 'default');
  const match = typicalDuration.match(/(\d+)/);
  if (match) {
    durationMinutes = parseInt(match[1]) * 60;
  }
}

Key Improvements

Better Logging

Added comprehensive console logs for debugging:

console.log(`\n=== Processing Day ${day.day_number} ===`);
console.log(`Remaining slots: ${remainingSlots}`);
console.log(`✓ Added: ${place.name} (type: ${place.type})`);
console.log(`⊘ Skipping ${place.name} (consecutive type)`);

Error Handling

Continue processing even if individual inserts fail:

if (placeError) {
  console.error(`✗ Insert error for ${place.name}:`, placeError.message);
  // Continue even if error (might be duplicate)
} else {
  console.log(`✓ Inserted: ${place.name}`);
}

Validation Warnings

Alert when minimum requirements not met:

if (dayPlaces.length < MIN_PLACES_PER_DAY) {
  console.warn(`⚠ Day ${day.day_number} has only ${dayPlaces.length} places (min: ${MIN_PLACES_PER_DAY})`);
}

Expected Behavior After Fix

Before Fix

  • Day 1: 2 places (balloon + 1 other)
  • Day 2: 1 place
  • Day 3: 2 places
  • Total: Sparse timeline, poor user experience

After Fix

  • Day 1: 5 places (balloon + 4 others)
  • Day 2: 5 places
  • Day 3: 5 places
  • Total: Full daily itineraries, rich experience

Files Modified

  1. src/db/api.ts - generateAutoSeedItinerary() function

    • Fixed targetPlaces calculation
    • Fixed order_index logic
    • Added duration parsing
    • Improved logging and error handling
  2. src/config/cappadocia-rules.ts

    • Changed MIN_PLACES_PER_DAY from 2 to 3
    • Relaxed isValidForDay() to allow non-consecutive same types
    • Changed signature to use lastPlaceType instead of usedTypesInDay

Testing Checklist

  • Create new trip with balloon interest
  • Verify each day has 3-5 places
  • Verify balloon has order_index = 0
  • Verify other places have sequential order_index (1, 2, 3, 4)
  • Verify no duplicate place_id across days
  • Verify same type can appear in same day if not consecutive
  • Verify duration is stored as integer minutes
  • Check console logs for detailed execution flow

Architecture Reminder

places (global catalog)
  ↓ read-only
trip_places (itinerary timeline)
  ↓ per trip, per day
Timeline UI (displays joined data)

Critical: Timeline UI reads ONLY from trip_places joined with places. The fix ensures trip_places is correctly populated with 3-5 places per day.