Reorder whole days and insert a day (#589) (#1148)

* feat(days): reorder whole days and insert a day at a position

Adds reorderDays + insertDay to the day service and a PUT /days/reorder route
(plus an optional position on create). Day rows stay stable so a day's
assignments, notes, bookings and accommodations ride along by id; on a dated
trip the calendar dates stay pinned to their slots while the content moves
across them, and each booking's date is re-stamped onto its day's new date
(time-of-day preserved) so day_id stays consistent. Renumbering uses the
two-phase write to avoid the UNIQUE(trip_id, day_number) collision, and a move
that would invert an accommodation's check-in/out span is rejected.

* feat(planner): reorder days from a toolbar popup, and add days

A new toolbar button opens a popup listing the days; drag a row by its grip or
use the up/down arrows to reorder, and add a day from there. Reorders apply
optimistically with rollback and sync over WebSocket; the day headers are left
untouched, so the existing place drop-targets are unaffected.

* i18n: add day-reorder strings across all languages
This commit is contained in:
Maurice
2026-06-12 00:17:49 +02:00
committed by GitHub
parent 1378c95078
commit f46cc8a98e
34 changed files with 872 additions and 9 deletions
+18 -1
View File
@@ -541,6 +541,23 @@ export function useTripPlanner() {
catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) }
}, [tripId, toast])
const handleReorderDays = useCallback((orderedIds: number[]) => {
const prevIds = (useTripStore.getState().days || [])
.slice().sort((a, b) => (a.day_number ?? 0) - (b.day_number ?? 0)).map(d => d.id)
tripActions.reorderDays(tripId, orderedIds)
.then(() => {
pushUndo(t('dayplan.reorderUndo'), async () => {
await tripActions.reorderDays(tripId, prevIds)
})
})
.catch(err => toast.error(err instanceof Error ? err.message : t('dayplan.reorderError')))
}, [tripId, toast, pushUndo])
const handleAddDay = useCallback((position?: number) => {
tripActions.insertDay(tripId, position)
.catch(err => toast.error(err instanceof Error ? err.message : t('dayplan.addDayError')))
}, [tripId, toast])
const handleSaveReservation = async (data: Record<string, string | number | null> & { title: string }) => {
try {
if (editingReservation) {
@@ -661,7 +678,7 @@ export function useTripPlanner() {
route, routeSegments, routeInfo, setRoute, setRouteInfo, updateRouteForDay,
handleSelectDay, handlePlaceClick, handleMarkerClick, handleMapClick, handleMapContextMenu, openAddPlaceFromPoi,
handleSavePlace, handleDeletePlace, confirmDeletePlace, confirmDeletePlaces,
handleAssignToDay, handleRemoveAssignment, handleReorder, handleUpdateDayTitle,
handleAssignToDay, handleRemoveAssignment, handleReorder, handleReorderDays, handleAddDay, handleUpdateDayTitle,
handleSaveReservation, handleSaveTransport, handleDeleteReservation,
selectedPlace, dayOrderMap, dayPlaces,
mapTileUrl, defaultCenter, defaultZoom, fontStyle, splashDone,