feat: show transport bookings in day plan timeline — closes #37
Transport reservations (flights, trains, buses, cars, cruises) now appear directly in the day plan timeline based on their reservation date/time. - Transport cards display inline with places and notes, sorted by time - Click to open detail modal with all booking data and linked files - Persistent positioning via new day_plan_position field on reservations - Free drag & drop: places can be moved between/around transport entries - Arrow reorder works on the full visual list including transports - Timed places show confirmation popup when reorder breaks chronology - Custom delete confirmation popup for reservations - DB migration adds day_plan_position column to reservations table - New batch endpoint PUT /reservations/positions for position updates - i18n keys added for DE and EN
This commit is contained in:
@@ -321,6 +321,10 @@ function runMigrations(db: Database.Database): void {
|
||||
UNIQUE(file_id, place_id)
|
||||
)`);
|
||||
},
|
||||
() => {
|
||||
// Add day_plan_position to reservations for persistent transport ordering in day timeline
|
||||
try { db.exec('ALTER TABLE reservations ADD COLUMN day_plan_position REAL DEFAULT NULL'); } catch {}
|
||||
},
|
||||
];
|
||||
|
||||
if (currentVersion < migrations.length) {
|
||||
|
||||
@@ -103,6 +103,29 @@ router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
broadcast(tripId, 'reservation:created', { reservation }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
// Batch update day_plan_position for multiple reservations (must be before /:id)
|
||||
router.put('/positions', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { positions } = req.body;
|
||||
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
if (!Array.isArray(positions)) return res.status(400).json({ error: 'positions must be an array' });
|
||||
|
||||
const stmt = db.prepare('UPDATE reservations SET day_plan_position = ? WHERE id = ? AND trip_id = ?');
|
||||
const updateMany = db.transaction((items: { id: number; day_plan_position: number }[]) => {
|
||||
for (const item of items) {
|
||||
stmt.run(item.day_plan_position, item.id, tripId);
|
||||
}
|
||||
});
|
||||
updateMany(positions);
|
||||
|
||||
res.json({ success: true });
|
||||
broadcast(tripId, 'reservation:positions', { positions }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
Reference in New Issue
Block a user