wip: backport form handing system from create flow to edit flow
This commit is contained in:
@@ -7,10 +7,13 @@ use App\BusProNet\Exception\ApiClientException;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\BusProNet\XmlLoader\PickupLoader;
|
||||
use App\Controller\Traits\BookingDataTrait;
|
||||
use App\Controller\Traits\HtmxControllerTrait;
|
||||
use App\Entity\User;
|
||||
use App\Form\BookingEditType;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Security\Crypt;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\BookingService;
|
||||
use App\Service\TravelDataService;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -25,10 +28,13 @@ use Symfony\Contracts\Cache\CacheInterface;
|
||||
class EditController extends AbstractController
|
||||
{
|
||||
use BookingDataTrait;
|
||||
use HtmxControllerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiClient $apiClient,
|
||||
private readonly TravelDataService $travelDataService,
|
||||
private readonly BookingService $bookingService,
|
||||
private readonly BookingPriceCalculatorService $priceCalculator,
|
||||
private readonly PickupLoader $pickupDataLoader,
|
||||
private readonly CacheInterface $cache,
|
||||
private readonly Security $security,
|
||||
@@ -83,6 +89,18 @@ class EditController extends AbstractController
|
||||
// Create DTO for form
|
||||
$formData = BookingEditDto::fromBooking($bookingData, $travelData);
|
||||
|
||||
// Calculate pricing data for template
|
||||
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($formData);
|
||||
$roomAssignmentCounts = $this->bookingService->getRoomAssignmentCounts($formData);
|
||||
$participantPrices = $this->priceCalculator->calculateAllParticipantIndividualPrices($formData);
|
||||
|
||||
// Group selected rooms for summary display
|
||||
$availableRooms = $formData->travel->getAvailableRooms();
|
||||
$groupedSelectedRooms = $this->bookingService->groupRoomSelectionsByType(
|
||||
$formData->getSelectedRooms(),
|
||||
$availableRooms
|
||||
);
|
||||
|
||||
$form = $this->createForm(BookingEditType::class, $formData, [
|
||||
'attr' => ['novalidate' => 'novalidate'],
|
||||
'validation_groups' => ['booking_edit'],
|
||||
@@ -132,10 +150,130 @@ class EditController extends AbstractController
|
||||
|
||||
return $this->render('booking/edit.html.twig', [
|
||||
'bookingData' => $bookingData,
|
||||
'bookingEditDto' => $formData,
|
||||
'travelData' => $travelData,
|
||||
'mutableData' => $mutableData,
|
||||
'availabilities' => $availabilities,
|
||||
'form' => $form->createView(),
|
||||
'pricingData' => $summary['pricing'],
|
||||
'participantCount' => $summary['participantCount'],
|
||||
'assignmentCounts' => $roomAssignmentCounts,
|
||||
'participantPrices' => $participantPrices,
|
||||
'groupedSelectedRooms' => $groupedSelectedRooms,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTMX endpoint for refreshing the participant form without validation.
|
||||
*/
|
||||
#[Route('/bookings/{id}/edit/refresh', name: 'app_booking_edit_refresh', requirements: ['id' => '\d+'], methods: ['POST'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function refreshParticipantForm(int $id, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
$email = $user->getEmail();
|
||||
$password = $this->crypt->decrypt($user->getPassword());
|
||||
|
||||
// Fetch original booking data via API and cache result for a short ttl
|
||||
$bookingData = $this->fetchBookingData($email, $password, $id);
|
||||
|
||||
if (null === $bookingData || $bookingData instanceof Notification) {
|
||||
return new Response('Buchungsdaten nicht verfügbar', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('EDIT', $bookingData);
|
||||
|
||||
// Load travel data
|
||||
$travelData = $this->travelDataService->getTravelData($bookingData->dateId);
|
||||
|
||||
if (null === $travelData) {
|
||||
return new Response('Reisedaten nicht verfügbar', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Fetch mutability and availability information
|
||||
$mutableData = $this->travelDataService->getMutabilityData($bookingData->dateId);
|
||||
$availabilities = $this->travelDataService->getAvailabilityDataCached($bookingData->dateId);
|
||||
|
||||
if (null === $mutableData || null === $availabilities) {
|
||||
return new Response('Reisedaten nicht verfügbar', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Patch travel data
|
||||
$this->travelDataService->patchAvailabilities($travelData, $availabilities);
|
||||
$this->travelDataService->patchMutability($travelData, $mutableData);
|
||||
|
||||
// Create DTO for form
|
||||
$formData = BookingEditDto::fromBooking($bookingData, $travelData);
|
||||
|
||||
// Process form data without validation to capture current state
|
||||
$form = $this->createForm(BookingEditType::class, $formData, [
|
||||
'attr' => ['novalidate' => 'novalidate'],
|
||||
'validation_groups' => false,
|
||||
]);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Collect notifications from all participants
|
||||
$notifications = $this->collectParticipantNotifications($formData);
|
||||
|
||||
// Calculate pricing and summary data
|
||||
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($formData);
|
||||
$roomAssignmentCounts = $this->bookingService->getRoomAssignmentCounts($formData);
|
||||
$participantPrices = $this->priceCalculator->calculateAllParticipantIndividualPrices($formData);
|
||||
|
||||
// Group selected rooms for summary display
|
||||
$availableRooms = $formData->travel->getAvailableRooms();
|
||||
$groupedSelectedRooms = $this->bookingService->groupRoomSelectionsByType(
|
||||
$formData->getSelectedRooms(),
|
||||
$availableRooms
|
||||
);
|
||||
|
||||
// Render updated blocks with fresh data
|
||||
$response = $this->htmxOobResponse(
|
||||
'booking/edit.html.twig',
|
||||
['participants_form', 'booking_summary'],
|
||||
[
|
||||
'form' => $form->createView(),
|
||||
'bookingEditDto' => $formData,
|
||||
'bookingData' => $bookingData,
|
||||
'pricingData' => $summary['pricing'],
|
||||
'participantCount' => $summary['participantCount'],
|
||||
'assignmentCounts' => $roomAssignmentCounts,
|
||||
'participantPrices' => $participantPrices,
|
||||
'groupedSelectedRooms' => $groupedSelectedRooms,
|
||||
]
|
||||
);
|
||||
|
||||
// Add notifications to HTMX trigger header if any exist
|
||||
if ([] !== $notifications) {
|
||||
$response->headers->set('HX-Trigger', json_encode([
|
||||
'showNotifications' => ['notifications' => $notifications],
|
||||
]));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all notifications from participants and clears them.
|
||||
*
|
||||
* @return array<array{type: string, message: string}> Array of notification messages
|
||||
*/
|
||||
private function collectParticipantNotifications(BookingEditDto $bookingEditDto): array
|
||||
{
|
||||
$notifications = [];
|
||||
|
||||
foreach ($bookingEditDto->participants as $participant) {
|
||||
if ([] !== $participant->notifications) {
|
||||
foreach ($participant->notifications as $notification) {
|
||||
$notifications[] = $notification;
|
||||
}
|
||||
// Clear notifications after collecting
|
||||
$participant->notifications = [];
|
||||
}
|
||||
}
|
||||
|
||||
return $notifications;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user