'\d+'])] #[IsGranted('ROLE_USER')] public function start(int $bookingId, Request $request): Response { /** @var User $user */ $user = $this->getUser(); $this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT); $this->dataLoader->invalidateBookingCache($bookingId, $user); return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]); } /** * Display participant cards overview. */ #[Route('/bookings/{bookingId}/edit', name: 'app_booking_edit', requirements: ['bookingId' => '\d+'])] #[IsGranted('ROLE_USER')] public function index(int $bookingId, Request $request): Response { /** @var User $user */ $user = $this->getUser(); // Load form data from session (or API on first load) try { $bookingDto = $this->dataLoader->loadFormData($request, $bookingId, $user); } catch (TravelNotFoundException) { $this->addFlash('error', 'Reisedaten sind nicht (mehr) verfügbar'); return $this->redirectToRoute('app_bookings'); } if (null === $bookingDto) { $this->addFlash('error', 'Buchungsdaten nicht (mehr) verfügbar'); return $this->redirectToRoute('app_bookings'); } // Show flash message if draft was restored if (true === $this->dataLoader->isDraftRestored()) { $this->addFlash('info', 'Dein zuvor gespeicherter Entwurf wurde wiederhergestellt.'); } // Fetch booking data for display (surcharges, canceled status, etc.) $bookingData = $this->dataLoader->fetchBookingData($bookingId, $user); if (null === $bookingData || $bookingData instanceof Notification) { $this->addFlash('error', 'Buchungsdaten nicht (mehr) verfügbar'); return $this->redirectToRoute('app_bookings'); } // Create validation form (same pattern as CreateStep2Controller) $form = $this->createForm(BookingEditType::class, $bookingDto); $form->handleRequest($request); // Handle form submission (clicking "Buchung aktualisieren") if ($form->isSubmitted() && $form->isValid()) { $submissionResult = $this->formSubmitter->handleSubmission($request, $bookingDto, $bookingId, $user); $this->applySubmissionResultFlashes($submissionResult); return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]); } $bookingEditContext = $this->editContextFactory->createOverviewContext( $bookingDto, $bookingData, $this->fingerprintService->isDirty($bookingDto), $form->isSubmitted(), $form->isSubmitted() && false === $form->isValid(), ); $templateData = [ 'form' => $form->createView(), 'bookingEditContext' => $bookingEditContext, ]; return $this->render('booking/edit/index.html.twig', $templateData); } private function applySubmissionResultFlashes(BookingEditSubmissionResult $result): void { if (true === $result->immutableChangesReverted) { $this->addFlash('info', 'Einige Änderungen wurden verworfen, da sie aktuell nicht mehr änderbar sind.'); } switch ($result->status) { case BookingEditSubmissionResult::STATUS_BOOKING_DATA_RELOAD_FAILED: $this->addFlash('error', 'Buchungsdaten konnten vor dem Speichern nicht neu geladen werden'); break; case BookingEditSubmissionResult::STATUS_NOTIFICATION_ERROR: $this->addFlash('error', $result->message ?? 'Es ist ein Fehler in der Kommunikation mit dem Buchungssystem aufgetreten'); break; case BookingEditSubmissionResult::STATUS_NOTIFICATION_INFO: if (null !== $result->message && '' !== trim($result->message)) { $this->addFlash('info', $result->message); } break; case BookingEditSubmissionResult::STATUS_SUCCESS: $this->addFlash('success', 'Buchung erfolgreich aktualisiert'); $this->addFlash('booking_edit_notice', 'Bitte ladet euch eine aktualisierte Rechnung herunter, damit ihr stets den aktuellen Stand eurer Buchung vorliegen habt.'); break; case BookingEditSubmissionResult::STATUS_UNSUCCESSFUL: $this->addFlash('error', $result->message ?? 'Buchung konnte nicht aktualisiert werden'); break; case BookingEditSubmissionResult::STATUS_TIMEOUT: $this->addFlash('error', 'Die Anfrage hat zu lange gedauert. Bitte versuche es erneut.'); break; case BookingEditSubmissionResult::STATUS_API_CLIENT_ERROR: $this->addFlash('error', 'Es ist ein Fehler in der Kommunikation mit dem Buchungssystem aufgetreten'); break; } } /** * Reloads booking data from API, discarding all session changes. * * GET: Returns modal HTML for confirmation * POST: Clears session and redirects to reload the booking */ #[Route( path: '/bookings/{bookingId}/edit/reload', name: 'app_booking_edit_reload', requirements: ['bookingId' => '\d+'] )] #[IsGranted('ROLE_USER')] public function reloadFromApi(int $bookingId, Request $request): Response { if (Request::METHOD_POST === $request->getMethod()) { // Clear session to discard all changes $this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT); // Delete draft since user explicitly chose to discard changes /** @var User $user */ $user = $this->getUser(); $this->draftService->deleteDraft($user, $bookingId); $this->addFlash('success', 'Änderungen verworfen, Daten neu geladen'); return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]); } return $this->render('booking/edit/modal_reload.html.twig', [ 'bookingId' => $bookingId, ]); } /** * Handles "Zurück" button - clears session and preserves draft. * * Draft is preserved so the user can continue editing later. * Shows a flash message informing about the saved draft. */ #[Route( path: '/bookings/{bookingId}/edit/cancel', name: 'app_booking_edit_cancel', requirements: ['bookingId' => '\d+'] )] #[IsGranted('ROLE_USER')] public function cancelEdit(int $bookingId, Request $request): Response { // Clear session state $this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT); // Check if a draft exists to show appropriate message /** @var User $user */ $user = $this->getUser(); $draft = $this->draftService->findDraft($user, $bookingId); if (null !== $draft) { $this->addFlash('info', 'Deine Änderungen wurden als Entwurf gespeichert.'); } return $this->redirectToRoute('app_bookings'); } }