mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 14:21:46 +00:00
4680aa254d
- Map tooltips now respect light/dark mode via CSS variables - Journey creation inherits cover image from first selected trip - Only day-assigned places are synced to journey (no unplanned places) - Place count in trip picker reflects assigned places only - Contributor avatars shown in journey detail page - Suggestion banner button visible in dark mode (!important override) - Dashboard list view uses correct trips array and status label
138 lines
6.1 KiB
TypeScript
138 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 {
|
|
getAssignmentWithPlace,
|
|
listDayAssignments,
|
|
dayExists,
|
|
placeExists,
|
|
createAssignment,
|
|
assignmentExistsInDay,
|
|
deleteAssignment,
|
|
reorderAssignments,
|
|
getAssignmentForTrip,
|
|
moveAssignment,
|
|
getParticipants,
|
|
updateTime,
|
|
setParticipants,
|
|
} from '../services/assignmentService';
|
|
import { onPlaceCreated } from '../services/journeyService';
|
|
import { AuthRequest } from '../types';
|
|
|
|
const router = express.Router({ mergeParams: true });
|
|
|
|
router.get('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const { tripId, dayId } = req.params;
|
|
|
|
if (!dayExists(dayId, tripId)) return res.status(404).json({ error: 'Day not found' });
|
|
|
|
const result = listDayAssignments(dayId);
|
|
res.json({ assignments: result });
|
|
});
|
|
|
|
router.post('/trips/:tripId/days/:dayId/assignments', 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, dayId } = req.params;
|
|
const { place_id, notes } = req.body;
|
|
|
|
if (!dayExists(dayId, tripId)) return res.status(404).json({ error: 'Day not found' });
|
|
if (!placeExists(place_id, tripId)) return res.status(404).json({ error: 'Place not found' });
|
|
|
|
const assignment = createAssignment(dayId, place_id, notes);
|
|
res.status(201).json({ assignment });
|
|
broadcast(tripId, 'assignment:created', { assignment }, req.headers['x-socket-id'] as string);
|
|
try { onPlaceCreated(Number(tripId), Number(place_id)); } catch {}
|
|
});
|
|
|
|
router.delete('/trips/:tripId/days/:dayId/assignments/: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, dayId, id } = req.params;
|
|
|
|
if (!assignmentExistsInDay(id, dayId, tripId)) return res.status(404).json({ error: 'Assignment not found' });
|
|
|
|
deleteAssignment(id);
|
|
res.json({ success: true });
|
|
broadcast(tripId, 'assignment:deleted', { assignmentId: Number(id), dayId: Number(dayId) }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.put('/trips/:tripId/days/:dayId/assignments/reorder', 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, dayId } = req.params;
|
|
const { orderedIds } = req.body;
|
|
|
|
if (!dayExists(dayId, tripId)) return res.status(404).json({ error: 'Day not found' });
|
|
|
|
reorderAssignments(dayId, orderedIds);
|
|
res.json({ success: true });
|
|
broadcast(tripId, 'assignment:reordered', { dayId: Number(dayId), orderedIds }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.put('/trips/:tripId/assignments/:id/move', 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 { new_day_id, order_index } = req.body;
|
|
|
|
const existing = getAssignmentForTrip(id, tripId);
|
|
if (!existing) return res.status(404).json({ error: 'Assignment not found' });
|
|
|
|
if (!dayExists(new_day_id, tripId)) return res.status(404).json({ error: 'Target day not found' });
|
|
|
|
const oldDayId = existing.day_id;
|
|
const { assignment: updated } = moveAssignment(id, new_day_id, order_index, oldDayId);
|
|
res.json({ assignment: updated });
|
|
broadcast(tripId, 'assignment:moved', { assignment: updated, oldDayId: Number(oldDayId), newDayId: Number(new_day_id) }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.get('/trips/:tripId/assignments/:id/participants', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
|
const { id } = req.params;
|
|
|
|
const participants = getParticipants(id);
|
|
res.json({ participants });
|
|
});
|
|
|
|
router.put('/trips/:tripId/assignments/:id/time', 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 = getAssignmentForTrip(id, tripId);
|
|
if (!existing) return res.status(404).json({ error: 'Assignment not found' });
|
|
|
|
const { place_time, end_time } = req.body;
|
|
const updated = updateTime(id, place_time, end_time);
|
|
res.json({ assignment: updated });
|
|
broadcast(Number(tripId), 'assignment:updated', { assignment: updated }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
router.put('/trips/:tripId/assignments/:id/participants', 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 { user_ids } = req.body;
|
|
if (!Array.isArray(user_ids)) return res.status(400).json({ error: 'user_ids must be an array' });
|
|
|
|
const participants = setParticipants(id, user_ids);
|
|
res.json({ participants });
|
|
broadcast(Number(tripId), 'assignment:participants', { assignmentId: Number(id), participants }, req.headers['x-socket-id'] as string);
|
|
});
|
|
|
|
export default router;
|