feat: support check-in time ranges for hotel accommodations

- Add check_in_end column to day_accommodations (Migration 102)
- Server: create/update accommodation accepts check_in_end
- Bidirectional sync: check_in_end synced between accommodation
  and linked reservation metadata (check_in_end_time)
- DayDetailPanel: shows check-in range (e.g. "14:00 – 22:00"),
  new "Until" time picker in hotel form
- ReservationModal: new check-in-until field for hotel bookings
- ReservationsPanel: displays check-in range in metadata cells
- i18n: checkInUntil keys in all 15 languages

Closes #366
This commit is contained in:
Maurice
2026-04-16 00:23:00 +02:00
parent 125436fa87
commit 409a63633c
24 changed files with 84 additions and 27 deletions
+4 -4
View File
@@ -73,7 +73,7 @@ accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request, r
return res.status(403).json({ error: 'No permission' });
const { tripId } = req.params;
const { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes } = req.body;
const { place_id, start_day_id, end_day_id, check_in, check_in_end, check_out, confirmation, notes } = req.body;
if (!place_id || !start_day_id || !end_day_id) {
return res.status(400).json({ error: 'place_id, start_day_id, and end_day_id are required' });
@@ -82,7 +82,7 @@ accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request, r
const errors = dayService.validateAccommodationRefs(tripId, place_id, start_day_id, end_day_id);
if (errors.length > 0) return res.status(404).json({ error: errors[0].message });
const accommodation = dayService.createAccommodation(tripId, { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes });
const accommodation = dayService.createAccommodation(tripId, { place_id, start_day_id, end_day_id, check_in, check_in_end, check_out, confirmation, notes });
res.status(201).json({ accommodation });
broadcast(tripId, 'accommodation:created', { accommodation }, req.headers['x-socket-id'] as string);
broadcast(tripId, 'reservation:created', {}, req.headers['x-socket-id'] as string);
@@ -98,12 +98,12 @@ accommodationsRouter.put('/:id', authenticate, requireTripAccess, (req: Request,
const existing = dayService.getAccommodation(id, tripId);
if (!existing) return res.status(404).json({ error: 'Accommodation not found' });
const { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes } = req.body;
const { place_id, start_day_id, end_day_id, check_in, check_in_end, check_out, confirmation, notes } = req.body;
const errors = dayService.validateAccommodationRefs(tripId, place_id, start_day_id, end_day_id);
if (errors.length > 0) return res.status(404).json({ error: errors[0].message });
const accommodation = dayService.updateAccommodation(id, existing, { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes });
const accommodation = dayService.updateAccommodation(id, existing, { place_id, start_day_id, end_day_id, check_in, check_in_end, check_out, confirmation, notes });
res.json({ accommodation });
broadcast(tripId, 'accommodation:updated', { accommodation }, req.headers['x-socket-id'] as string);
});