mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
409a63633c
- 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
131 lines
6.1 KiB
TypeScript
131 lines
6.1 KiB
TypeScript
import express, { Request, Response } from 'express';
|
|
import { authenticate } from '../middleware/auth';
|
|
import { requireTripAccess } from '../middleware/tripAccess';
|
|
import { broadcast } from '../websocket';
|
|
import { checkPermission } from '../services/permissions';
|
|
import { AuthRequest } from '../types';
|
|
import * as dayService from '../services/dayService';
|
|
|
|
const router = express.Router({ mergeParams: true });
|
|
|
|
router.get('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const { tripId } = req.params;
|
|
res.json(dayService.listDays(tripId));
|
|
});
|
|
|
|
router.post('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId } = req.params;
|
|
const { date, notes } = req.body;
|
|
|
|
const day = dayService.createDay(tripId, date, notes);
|
|
res.status(201).json({ day });
|
|
broadcast(tripId, 'day:created', { day }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.put('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId, id } = req.params;
|
|
|
|
const current = dayService.getDay(id, tripId);
|
|
if (!current) return res.status(404).json({ error: 'Day not found' });
|
|
|
|
const { notes, title } = req.body;
|
|
const day = dayService.updateDay(id, current, { notes, title });
|
|
res.json({ day });
|
|
broadcast(tripId, 'day:updated', { day }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId, id } = req.params;
|
|
|
|
if (!dayService.getDay(id, tripId)) return res.status(404).json({ error: 'Day not found' });
|
|
|
|
dayService.deleteDay(id);
|
|
res.json({ success: true });
|
|
broadcast(tripId, 'day:deleted', { dayId: Number(id) }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Accommodations sub-router
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const accommodationsRouter = express.Router({ mergeParams: true });
|
|
|
|
accommodationsRouter.get('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const { tripId } = req.params;
|
|
res.json({ accommodations: dayService.listAccommodations(tripId) });
|
|
});
|
|
|
|
accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId } = req.params;
|
|
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' });
|
|
}
|
|
|
|
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_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);
|
|
});
|
|
|
|
accommodationsRouter.put('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId, id } = req.params;
|
|
|
|
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_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_in_end, check_out, confirmation, notes });
|
|
res.json({ accommodation });
|
|
broadcast(tripId, 'accommodation:updated', { accommodation }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
accommodationsRouter.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const authReq = req as AuthRequest;
|
|
if (!checkPermission('day_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id))
|
|
return res.status(403).json({ error: 'No permission' });
|
|
|
|
const { tripId, id } = req.params;
|
|
|
|
if (!dayService.getAccommodation(id, tripId)) return res.status(404).json({ error: 'Accommodation not found' });
|
|
|
|
const { linkedReservationId } = dayService.deleteAccommodation(id);
|
|
if (linkedReservationId) {
|
|
broadcast(tripId, 'reservation:deleted', { reservationId: linkedReservationId }, req.headers['x-socket-id'] as string);
|
|
}
|
|
|
|
res.json({ success: true });
|
|
broadcast(tripId, 'accommodation:deleted', { accommodationId: Number(id) }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
export default router;
|
|
export { accommodationsRouter };
|