2025-10-06 14:21:47 +02:00
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
import DaySection from './DaySection';
|
|
|
|
|
import axios from 'axios';
|
2025-10-07 22:51:10 +02:00
|
|
|
import './TripTimeline.css';
|
2025-10-06 14:21:47 +02:00
|
|
|
|
|
|
|
|
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
|
|
|
|
|
|
|
|
|
function TripTimeline({ trip, plannableItems, onScheduleSuccess }) {
|
|
|
|
|
const [calendarSlots, setCalendarSlots] = useState([]);
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetchCalendarSlots();
|
|
|
|
|
}, [trip.id]);
|
|
|
|
|
|
|
|
|
|
const fetchCalendarSlots = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('token');
|
|
|
|
|
const response = await axios.get(`${API_URL}/api/trips/${trip.id}/calendar-slots`, {
|
|
|
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
|
|
|
});
|
2025-10-07 22:51:10 +02:00
|
|
|
const slots = response.data.data || [];
|
|
|
|
|
setCalendarSlots(slots);
|
2025-10-06 14:21:47 +02:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error fetching calendar slots:', error);
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleScheduleItem = async (plannableItemId, startDatetime, endDatetime) => {
|
|
|
|
|
try {
|
|
|
|
|
const token = localStorage.getItem('token');
|
2025-10-07 22:51:10 +02:00
|
|
|
const payload = {
|
|
|
|
|
plannable_item_id: plannableItemId,
|
|
|
|
|
trip_id: trip.id,
|
|
|
|
|
start_datetime: startDatetime,
|
|
|
|
|
end_datetime: endDatetime,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await axios.post(
|
2025-10-06 14:21:47 +02:00
|
|
|
`${API_URL}/api/planned-items`,
|
2025-10-07 22:51:10 +02:00
|
|
|
payload,
|
2025-10-06 14:21:47 +02:00
|
|
|
{
|
|
|
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await fetchCalendarSlots();
|
|
|
|
|
if (onScheduleSuccess) {
|
|
|
|
|
onScheduleSuccess();
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error scheduling item:', error);
|
2025-10-07 22:51:10 +02:00
|
|
|
console.error('Error response:', error.response?.data);
|
2025-10-06 14:21:47 +02:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const generateDays = () => {
|
|
|
|
|
const days = [];
|
|
|
|
|
const start = new Date(trip.start_date);
|
|
|
|
|
const end = new Date(trip.end_date);
|
|
|
|
|
|
2025-10-07 22:51:10 +02:00
|
|
|
for (let d = new Date(start); d <= end; ) {
|
2025-10-06 14:21:47 +02:00
|
|
|
days.push(new Date(d));
|
2025-10-07 22:51:10 +02:00
|
|
|
d.setDate(d.getDate() + 1);
|
2025-10-06 14:21:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return days;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getSlotsForDay = (date) => {
|
2025-10-07 22:51:10 +02:00
|
|
|
// Format date as YYYY-MM-DD in local timezone, not UTC
|
|
|
|
|
const year = date.getFullYear();
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
|
|
|
const dateString = `${year}-${month}-${day}`;
|
|
|
|
|
|
|
|
|
|
const slots = calendarSlots.filter(slot => {
|
|
|
|
|
// slot_date comes as "2025-12-12T00:00:00.000000Z", extract just the date part
|
|
|
|
|
const slotDateString = slot.slot_date.split('T')[0];
|
|
|
|
|
return slotDateString === dateString;
|
|
|
|
|
});
|
|
|
|
|
return slots;
|
2025-10-06 14:21:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
2025-10-07 22:51:10 +02:00
|
|
|
return <div className="timeline-loading">Loading timeline...</div>;
|
2025-10-06 14:21:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const days = generateDays();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="trip-timeline">
|
2025-10-07 22:51:10 +02:00
|
|
|
<h3>Trip Timeline</h3>
|
|
|
|
|
<div className="timeline-days-container">
|
2025-10-06 14:21:47 +02:00
|
|
|
{days.map((day, index) => (
|
|
|
|
|
<DaySection
|
2025-10-07 22:51:10 +02:00
|
|
|
key={`${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`}
|
2025-10-06 14:21:47 +02:00
|
|
|
date={day}
|
|
|
|
|
dayNumber={index + 1}
|
|
|
|
|
slots={getSlotsForDay(day)}
|
|
|
|
|
plannableItems={plannableItems}
|
|
|
|
|
onScheduleItem={handleScheduleItem}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default TripTimeline;
|