feat: groups price calculator admin crud, booking/offer flow and api

This commit is contained in:
Björn Fromme committed 2026-08-03 15:25:10 +02:00
1 parent eabed8295a
commit 9d2aa11fdb
258 files changed
+16231 -582

No files matched your search

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Accommodation;
use App\Entity\Groups\Accommodation;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class CalendarController extends AbstractController
{
#[Route('/admin/accommodation/{id}/calendar', name: 'app_admin_accommodation_calendar', methods: ['GET'])]
public function index(Accommodation $accommodation, Request $request): Response
{
return $this->render('admin/accommodation/_calendar_section.html.twig', [
'accommodation' => $accommodation,
'startMonth' => $request->query->get('month'),
]);
}
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Accommodation;
use App\Entity\Groups\Accommodation;
use App\Form\Admin\Groups\AccommodationType;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation/create', name: 'app_admin_accommodation_create')]
public function index(Request $request): Response
{
$accommodation = new Accommodation();
$form = $this->createForm(AccommodationType::class, $accommodation, ['hx_post' => $request->getRequestUri()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($accommodation);
$this->entityManager->flush();
$this->addFlash('success', 'Das Gruppenhaus wurde angelegt');
$this->logger->info('Created accommodation', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
]);
return new HxRedirectResponse($this->generateUrl('app_admin_accommodation'));
}
return $this->render('admin/accommodation/modal_create.html.twig', [
'accommodation' => $accommodation,
'form' => $form,
]);
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Accommodation;
use App\Entity\Groups\Accommodation;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class DeleteController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation/{id}/delete', name: 'app_admin_accommodation_delete')]
public function index(Accommodation $accommodation, Request $request): Response
{
if (true === $request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('delete_accommodation_'.$accommodation->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->entityManager->remove($accommodation);
$this->entityManager->flush();
$this->addFlash('success', 'Das Gruppenhaus wurde gelöscht');
$this->logger->info('Deleted accommodation', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
]);
return new HxRedirectResponse($this->generateUrl('app_admin_accommodation'));
}
return $this->render('admin/accommodation/modal_delete.html.twig', [
'accommodation' => $accommodation,
'csrf_token_id' => 'delete_accommodation_'.$accommodation->getId(),
]);
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Accommodation;
use App\Entity\Groups\Accommodation;
use App\Form\Admin\Groups\AccommodationType;
use App\Service\CmsDataProvider;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class EditController extends AbstractController
{
public function __construct(
private readonly CmsDataProvider $cmsDataProvider,
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation/{id}/edit', name: 'app_admin_accommodation_edit')]
public function index(Accommodation $accommodation, Request $request): Response
{
// fetch additional data from cms if available
$cmsData = null;
if (null !== $accommodation->getCalendarCode()) {
$cmsData = $this->cmsDataProvider->getHotelDetails($accommodation->getEffectiveCmsCode());
}
$form = $this->createForm(AccommodationType::class, $accommodation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($accommodation);
$this->entityManager->flush();
$this->addFlash('success', 'Das Gruppenhaus wurde aktualisiert');
$this->logger->info('Updated accommodation', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
]);
return $this->redirectToRoute('app_admin_accommodation_edit', ['id' => $accommodation->getId()]);
}
return $this->render('admin/accommodation/edit.html.twig', [
'accommodation' => $accommodation,
'form' => $form,
'cmsData' => $cmsData,
]);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Accommodation;
use App\Repository\Groups\AccommodationRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class IndexController extends AbstractController
{
public function __construct(
private readonly AccommodationRepository $accommodationRepository,
) {
}
#[Route('/admin/accommodation', name: 'app_admin_accommodation')]
public function index(): Response
{
$accommodations = $this->accommodationRepository->findBy([], ['calendarCode' => 'ASC']);
return $this->render('admin/accommodation/index.html.twig', [
'accommodations' => $accommodations,
]);
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Entity\Groups\AccommodationBooking;
use App\Form\Admin\Groups\AccommodationBookingType;
use App\Service\AccommodationBookingService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
private readonly AccommodationBookingService $bookingService,
) {
}
#[Route('/admin/accommodation-booking/create', name: 'app_admin_accommodationbooking_create')]
public function index(Request $request): Response
{
$booking = new AccommodationBooking();
$form = $this->createForm(AccommodationBookingType::class, $booking, [
'with_accommodation' => true,
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->bookingService->refreshPriceSnapshot($booking);
$this->entityManager->persist($booking);
$this->entityManager->flush();
$this->bookingService->issueAccessLinkForDirectBooking($booking);
$this->bookingService->sendCustomerConfirmationEmail($booking);
$this->addFlash('success', 'Die Buchung wurde erstellt');
$this->logger->info('Created accommodation booking', [
'id' => $booking->getId(),
'groupName' => $booking->getGroupName(),
]);
return $this->redirectToRoute('app_admin_accommodationbooking_edit', ['id' => $booking->getId()]);
}
return $this->render('admin/accommodation_booking/create.html.twig', [
'form' => $form,
]);
}
}
@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Entity\Groups\AccommodationBooking;
use App\Entity\Groups\AdditionalService;
use App\Entity\Groups\BoardService;
use App\Form\Admin\Groups\AccommodationBookingType;
use App\Repository\Groups\AdditionalServiceRepository;
use App\Repository\Groups\BoardServiceRepository;
use App\Service\AccommodationBookingService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class EditController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
private readonly BoardServiceRepository $boardServiceRepo,
private readonly AdditionalServiceRepository $additionalServiceRepo,
private readonly AccommodationBookingService $bookingService,
) {
}
#[Route('/admin/accommodation-booking/{id}/edit', name: 'app_admin_accommodationbooking_edit')]
public function index(AccommodationBooking $booking, Request $request): Response
{
$accommodation = $booking->getAccommodation();
$boardServices = [];
$additionalServices = [];
$currentBoardService = null;
$currentAdditionalServices = [];
if (null !== $accommodation && null !== $booking->getDateFrom() && null !== $booking->getDateTo()) {
$boardServices = $this->boardServiceRepo->findByAccommodationAndDateRange(
$accommodation,
$booking->getDateFrom(),
$booking->getDateTo(),
);
$additionalServices = $this->additionalServiceRepo->findByAccommodationAndDateRange(
$accommodation,
$booking->getDateFrom(),
$booking->getDateTo(),
);
// Pre-select current board service if it is still in the available choices
foreach ($boardServices as $bs) {
if ($bs->getId() === $booking->getBoardServiceOriginalId()) {
$currentBoardService = $bs;
break;
}
}
// Pre-select current additional services that are still in the available choices
$currentIds = array_column($booking->getAdditionalServices(), 'originalServiceId');
$currentAdditionalServices = array_values(array_filter(
$additionalServices,
fn (AdditionalService $s) => in_array($s->getId(), $currentIds, true),
));
}
$form = $this->createForm(AccommodationBookingType::class, $booking, [
'max_adolescent_age' => $accommodation?->getMaxAdolescentAge() ?? 0,
'board_services' => $boardServices,
'additional_services' => $additionalServices,
'current_board_service' => $currentBoardService,
'current_additional_services' => $currentAdditionalServices,
]);
$wasInquiry = $booking->isInquiry();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($form->has('boardService')) {
$selectedBoardService = $form->get('boardService')->getData();
if ($selectedBoardService instanceof BoardService) {
$booking->setBoardServiceLabel($selectedBoardService->getLabel());
$booking->setBoardServicePrice($selectedBoardService->getPrice());
$booking->setBoardServiceOriginalId($selectedBoardService->getId());
} else {
$booking->setBoardServiceLabel(null);
$booking->setBoardServicePrice(null);
$booking->setBoardServiceOriginalId(null);
}
}
if ($form->has('selectedAdditionalServices')) {
$booking->setAdditionalServices([]);
foreach ($form->get('selectedAdditionalServices')->getData() as $service) {
$booking->addAdditionalServiceSnapshot(
$service->getLabel() ?? '',
$service->getPrice() ?? 0,
$service->getType(),
$service->getId(),
);
}
}
$this->bookingService->refreshPriceSnapshot($booking);
$this->entityManager->persist($booking);
$this->entityManager->flush();
if ($wasInquiry && !$booking->isInquiry()) {
$this->bookingService->issueAccessLinkForDirectBooking($booking);
$this->bookingService->sendCustomerConfirmationEmail($booking);
}
$this->addFlash('success', 'Die Buchung wurde aktualisiert');
$this->logger->info('Updated accommodation booking', [
'id' => $booking->getId(),
'groupName' => $booking->getGroupName(),
]);
return $this->redirectToRoute('app_admin_accommodationbooking_edit', ['id' => $booking->getId()]);
}
return $this->render('admin/accommodation_booking/edit.html.twig', [
'booking' => $booking,
'form' => $form,
]);
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Entity\Groups\AccommodationBooking;
use App\Htmx\HxRedirectResponse;
use App\Service\AccommodationBookingService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class GenerateAccessLinkController extends AbstractController
{
public function __construct(
private readonly AccommodationBookingService $bookingService,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-booking/{id}/generate-access-link', name: 'app_admin_accommodationbooking_generate_access_link')]
public function index(AccommodationBooking $booking, Request $request): Response
{
if ($request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('generate_accommodation_booking_access_link_'.$booking->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->bookingService->regenerateAccessLink($booking);
$this->addFlash('success', 'Der Zugangslink wurde neu generiert.');
$this->logger->info('Generated accommodation booking access link', [
'id' => $booking->getId(),
]);
return new HxRedirectResponse($this->generateUrl('app_admin_accommodationbooking_show', ['id' => $booking->getId()]));
}
return $this->render('admin/accommodation_booking/modal_generate_access_link.html.twig', [
'booking' => $booking,
'csrf_token_id' => 'generate_accommodation_booking_access_link_'.$booking->getId(),
]);
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Repository\Groups\AccommodationBookingRepository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class IndexController extends AbstractController
{
public function __construct(
private readonly AccommodationBookingRepository $bookingRepository,
private readonly PaginatorInterface $paginator,
) {
}
#[Route('/admin/accommodation-booking', name: 'app_admin_accommodationbooking')]
public function index(Request $request): Response
{
$today = new \DateTimeImmutable('today');
$qb = $this
->bookingRepository
->createQueryBuilder('booking')
->leftJoin('booking.accommodation', 'accommodation')
->addSelect('accommodation')
->where('booking.dateTo >= :today')
->setParameter('today', $today)
;
$pagination = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 20),
[
'defaultSortFieldName' => 'booking.createdAt',
'defaultSortDirection' => 'DESC',
]
);
return $this->render('admin/accommodation_booking/index.html.twig', [
'pagination' => $pagination,
]);
}
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Entity\Groups\AccommodationBooking;
use App\Htmx\HxRedirectResponse;
use App\Service\AccommodationBookingService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class SendAccessLinkController extends AbstractController
{
public function __construct(
private readonly AccommodationBookingService $bookingService,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-booking/{id}/send-access-link', name: 'app_admin_accommodationbooking_send_access_link')]
public function index(AccommodationBooking $booking, Request $request): Response
{
if (null === $booking->getAccessLinkIssuedAt()) {
return $this->redirectToRoute('app_admin_accommodationbooking_show', ['id' => $booking->getId()]);
}
if ($request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('send_accommodation_booking_access_link_'.$booking->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->bookingService->sendCustomerConfirmationEmail($booking);
$this->addFlash('success', 'Der Zugangslink wurde dem Kunden per E-Mail zugestellt.');
$this->logger->info('Sent accommodation booking access link', [
'id' => $booking->getId(),
]);
return new HxRedirectResponse($this->generateUrl('app_admin_accommodationbooking_show', ['id' => $booking->getId()]));
}
return $this->render('admin/accommodation_booking/modal_send_access_link.html.twig', [
'booking' => $booking,
'csrf_token_id' => 'send_accommodation_booking_access_link_'.$booking->getId(),
]);
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\Groups\AccommodationBooking;
use App\Service\AccommodationBookingBreakdownCalculator;
use App\Service\AccommodationBookingLinkSigner;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class ShowController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(
private readonly AccommodationBookingBreakdownCalculator $breakdownCalculator,
private readonly AccommodationBookingLinkSigner $linkSigner,
) {
}
#[Route('/admin/accommodation-booking/{id}', name: 'app_admin_accommodationbooking_show')]
public function index(AccommodationBooking $booking, Request $request): Response
{
$hasAccessLink = null !== $booking->getAccessLinkIssuedAt();
return $this->render('admin/accommodation_booking/show.html.twig', [
'booking' => $booking,
'priceBreakdown' => $this->breakdownCalculator->compute($booking),
'returnUrl' => $this->getReturnUrl($request, 'app_admin_accommodationbooking'),
'accessLink' => $hasAccessLink ? $this->linkSigner->sign($booking) : null,
'accessLinkExpiresAt' => $hasAccessLink ? $this->linkSigner->expiresAt($booking) : null,
]);
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationPrice;
use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AccommodationPrice;
use App\Form\Admin\Groups\AccommodationPriceType;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-price/{id}/create', name: 'app_admin_accommodationprice_create')]
public function index(Accommodation $accommodation, Request $request): Response
{
$accommodationPrice = new AccommodationPrice();
$form = $this->createForm(AccommodationPriceType::class, $accommodationPrice, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$accommodation->addAccommodationPrice($accommodationPrice);
$this->entityManager->persist($accommodationPrice);
$this->entityManager->flush();
$this->addFlash('success', 'Der Preis wurde angelegt');
$this->logger->info('Created accommodation price', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'date_range' => [
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
]
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#prices');
}
return $this->render('admin/accommodation_price/create.html.twig', [
'accommodation' => $accommodation,
'form' => $form,
]);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationPrice;
use App\Entity\Groups\AccommodationPrice;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class DeleteController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-price/{id}/delete', name: 'app_admin_accommodationprice_delete')]
public function index(AccommodationPrice $accommodationPrice, Request $request): Response
{
$accommodation = $accommodationPrice->getAccommodation();
if (true === $request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('delete_accommodation_price_'.$accommodationPrice->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->entityManager->remove($accommodationPrice);
$this->entityManager->flush();
$this->addFlash('success', 'Der Preis wurde gelöscht');
$this->logger->info('Deleted accommodation price', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'date_range' => [
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
],
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return new HxRedirectResponse($returnUrl.'#prices');
}
return $this->render('admin/accommodation_price/modal_delete.html.twig', [
'accommodationPrice' => $accommodationPrice,
'accommodation' => $accommodation,
'csrf_token_id' => 'delete_accommodation_price_'.$accommodationPrice->getId(),
]);
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationPrice;
use App\Entity\Groups\AccommodationPrice;
use App\Form\Admin\Groups\AccommodationPriceType;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class EditController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-price/{id}/edit', name: 'app_admin_accommodationprice_edit', defaults: ['duplicate' => false])]
#[Route('/admin/accommodation-price/{id}/duplicate', name: 'app_admin_accommodationprice_duplicate', defaults: ['duplicate' => true])]
public function index(AccommodationPrice $accommodationPrice, bool $duplicate, Request $request): Response
{
$accommodation = $accommodationPrice->getAccommodation();
if (true === $duplicate) {
$accommodationPrice = clone $accommodationPrice;
}
$form = $this->createForm(AccommodationPriceType::class, $accommodationPrice, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (true === $duplicate) {
$accommodation->addAccommodationPrice($accommodationPrice);
}
$this->entityManager->persist($accommodationPrice);
$this->entityManager->flush();
$this->addFlash('success', 'Der Preis wurde aktualisiert');
$this->logger->info('Edited accommodation price', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'date_range' => [
'from' => $accommodationPrice->getDateFrom()->format('Y-m-d'),
'to' => $accommodationPrice->getDateTo()->format('Y-m-d'),
]
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#prices');
}
return $this->render('admin/accommodation_price/edit.html.twig', [
'accommodation' => $accommodation,
'accommodationPrice' => $accommodationPrice,
'form' => $form,
'duplicate' => $duplicate,
]);
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AdditionalService;
use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AdditionalService;
use App\Form\Admin\Groups\AdditionalServiceType;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/additional-service/{id}/create', name: 'app_admin_additionalservice_create')]
public function index(Accommodation $accommodation, Request $request): Response
{
$additionalService = new AdditionalService();
$form = $this->createForm(AdditionalServiceType::class, $additionalService, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$accommodation->addAdditionalService($additionalService);
$this->entityManager->persist($additionalService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Zusatzleistung wurde angelegt');
$this->logger->info('Created additional service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $additionalService->getLabel(),
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#additional-services');
}
return $this->render('admin/additional_service/create.html.twig', [
'accommodation' => $accommodation,
'form' => $form,
]);
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AdditionalService;
use App\Entity\Groups\AdditionalService;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class DeleteController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/additional-service/{id}/delete', name: 'app_admin_additionalservice_delete')]
public function index(AdditionalService $additionalService, Request $request): Response
{
$accommodation = $additionalService->getAccommodation();
if (true === $request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('delete_additional_service_'.$additionalService->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->entityManager->remove($additionalService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Zusatzleistung wurde gelöscht');
$this->logger->info('Deleted additional service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $additionalService->getLabel(),
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return new HxRedirectResponse($returnUrl.'#additional-services');
}
return $this->render('admin/additional_service/modal_delete.html.twig', [
'additionalService' => $additionalService,
'accommodation' => $accommodation,
'csrf_token_id' => 'delete_additional_service_'.$additionalService->getId(),
]);
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AdditionalService;
use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AdditionalService;
use App\Form\Admin\Groups\AdditionalServiceType;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class EditController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/additional-service/{id}/edit', name: 'app_admin_additionalservice_edit', defaults: ['duplicate' => false])]
#[Route('/admin/additional-service/{id}/duplicate', name: 'app_admin_additionalservice_duplicate', defaults: ['duplicate' => true])]
public function index(AdditionalService $additionalService, bool $duplicate, Request $request): Response
{
$accommodation = $additionalService->getAccommodation();
if (true === $duplicate) {
$additionalService = clone $additionalService;
}
$form = $this->createForm(AdditionalServiceType::class, $additionalService, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (true === $duplicate) {
$accommodation->addAdditionalService($additionalService);
}
$this->entityManager->persist($additionalService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Zusatzleistung wurde aktualisiert');
$this->logger->info('Edited additional service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $additionalService->getLabel(),
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#additional-services');
}
return $this->render('admin/additional_service/edit.html.twig', [
'accommodation' => $accommodation,
'additionalService' => $additionalService,
'form' => $form,
'duplicate' => $duplicate,
]);
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BoardService;
use App\Entity\Groups\Accommodation;
use App\Entity\Groups\BoardService;
use App\Form\Admin\Groups\BoardServiceType;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/board-service/{id}/create', name: 'app_admin_boardservice_create')]
public function index(Accommodation $accommodation, Request $request): Response
{
$boardService = new BoardService();
$form = $this->createForm(BoardServiceType::class, $boardService, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$accommodation->addBoardService($boardService);
$this->entityManager->persist($boardService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Verpflegungsleistung wurde angelegt');
$this->logger->info('Created board service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $boardService->getLabel(),
]);
$returnUrl = $this->redirectToRoute('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#board-services');
}
return $this->render('admin/board_service/create.html.twig', [
'accommodation' => $accommodation,
'form' => $form,
]);
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BoardService;
use App\Entity\Groups\BoardService;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class DeleteController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/board-service/{id}/delete', name: 'app_admin_boardservice_delete')]
public function index(BoardService $boardService, Request $request): Response
{
$accommodation = $boardService->getAccommodation();
if (true === $request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('delete_board_service_'.$boardService->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->entityManager->remove($boardService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Verpflegungsleistung wurde gelöscht');
$this->logger->info('Deleted board service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $boardService->getLabel(),
]);
$returnUrl = $this->generateUrl('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return new HxRedirectResponse($returnUrl.'#board-services');
}
return $this->render('admin/board_service/modal_delete.html.twig', [
'boardService' => $boardService,
'accommodation' => $accommodation,
'csrf_token_id' => 'delete_board_service_'.$boardService->getId(),
]);
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BoardService;
use App\Entity\Groups\BoardService;
use App\Form\Admin\Groups\BoardServiceType;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class EditController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/board-service/{id}/edit', name: 'app_admin_boardservice_edit', defaults: ['duplicate' => false])]
#[Route('/admin/board-service/{id}/duplicate', name: 'app_admin_boardservice_duplicate', defaults: ['duplicate' => true])]
public function index(BoardService $boardService, bool $duplicate, Request $request): Response
{
$accommodation = $boardService->getAccommodation();
if (true === $duplicate) {
$boardService = clone $boardService;
}
$form = $this->createForm(BoardServiceType::class, $boardService, ['currency' => $accommodation->getCurrency()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (true === $duplicate) {
$accommodation->addBoardService($boardService);
}
$this->entityManager->persist($boardService);
$this->entityManager->flush();
$this->addFlash('success', 'Die Verpflegungsleistung wurde aktualisiert');
$this->logger->info('Edited board service', [
'name' => $accommodation->getName(),
'code' => $accommodation->getCalendarCode(),
'label' => $boardService->getLabel(),
]);
$returnUrl = $this->redirectToRoute('app_admin_accommodation_edit', [
'id' => $accommodation->getId(),
]);
return $this->redirect($returnUrl.'#board-services');
}
return $this->render('admin/board_service/edit.html.twig', [
'accommodation' => $accommodation,
'boardService' => $boardService,
'form' => $form,
'duplicate' => $duplicate,
]);
}
}
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BookingEditDraft;
use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\BookingEditDraft;
use App\Htmx\HxRedirectResponse;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class DeleteController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $adminLogger,
) {
}
#[Route('/admin/booking-edit-draft/{id}/delete', name: 'app_admin_bookingeditdraft_delete')]
public function index(BookingEditDraft $draft, Request $request): Response
{
if (true === $request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('delete_booking_edit_draft_'.$draft->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->entityManager->remove($draft);
$this->entityManager->flush();
$this->adminLogger->info('Delete booking edit draft', [
'booking_number' => $draft->getBookingNumber(),
]);
$this->addFlash('success', 'Der Buchungsentwurf wurde gelöscht');
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
return new HxRedirectResponse($returnUrl);
}
return $this->render('admin/booking_edit_draft/modal_delete.html.twig', [
'draft' => $draft,
'csrf_token_id' => 'delete_booking_edit_draft_'.$draft->getId(),
]);
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BookingEditDraft;
use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\BookingEditDraft;
use App\Service\BookingExporter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class ExportController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(private readonly BookingExporter $bookingExporter)
{
}
#[Route('/admin/booking-edit-draft/{id}/export', name: 'app_admin_bookingeditdraft_export')]
public function index(BookingEditDraft $draft): Response
{
if (false === $draft->hasExportData()) {
$this->addFlash('warning', 'Der Export ist fehlgeschlagen');
return $this->redirectToRoute('app_admin_bookingeditdraft');
}
try {
return $this->bookingExporter->createExportResponse($draft);
} catch (\RuntimeException $e) {
$this->addFlash('warning', 'Der Export ist fehlgeschlagen: '.$e->getMessage());
return $this->redirectToRoute('app_admin_bookingeditdraft');
}
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BookingEditDraft;
use App\Controller\Traits\ReturnUrlTrait;
use App\Repository\BookingEditDraftRepository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class IndexController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(
private readonly BookingEditDraftRepository $bookingEditDraftRepository,
private readonly PaginatorInterface $paginator,
) {
}
#[Route('/admin/booking-edit-draft', name: 'app_admin_bookingeditdraft')]
public function index(Request $request): Response
{
$qb = $this
->bookingEditDraftRepository
->createQueryBuilder('booking_edit_draft')
->leftJoin('booking_edit_draft.user', 'user')
;
$pagination = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 20),
[
'defaultSortFieldName' => 'booking_edit_draft.createdAt',
'defaultSortDirection' => 'DESC',
]
);
return $this->render('admin/booking_edit_draft/index.html.twig', [
'pagination' => $pagination,
]);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\BookingEditDraft;
use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\BookingEditDraft;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_ADMIN')]
class ShowController extends AbstractController
{
use ReturnUrlTrait;
#[Route('/admin/booking-edit-draft/{id}/show', name: 'app_admin_bookingeditdraft_show')]
public function index(BookingEditDraft $draft, Request $request): Response
{
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
return $this->render('admin/booking_edit_draft/show.html.twig', [
'draft' => $draft,
'returnUrl' => $returnUrl,
]);
}
}
@@ -1,107 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\BookingEditDraft;
use App\Htmx\HxRedirectResponse;
use App\Repository\BookingEditDraftRepository;
use App\Service\BookingExporter;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BookingEditDraftController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly BookingEditDraftRepository $bookingEditDraftRepository,
private readonly PaginatorInterface $paginator,
private readonly BookingExporter $bookingExporter,
private readonly LoggerInterface $adminLogger,
) {
}
#[Route('/admin/booking-edit-draft', name: 'app_admin_bookingeditdraft')]
public function index(Request $request): Response
{
$qb = $this
->bookingEditDraftRepository
->createQueryBuilder('booking_edit_draft')
->leftJoin('booking_edit_draft.user', 'user')
;
$pagination = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 20),
[
'defaultSortFieldName' => 'booking_edit_draft.createdAt',
'defaultSortDirection' => 'DESC',
]
);
return $this->render('admin/booking_edit_draft/index.html.twig', [
'pagination' => $pagination,
]);
}
#[Route('/admin/booking-edit-draft/{id}/show', name: 'app_admin_bookingeditdraft_show')]
public function show(BookingEditDraft $draft, Request $request): Response
{
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
return $this->render('admin/booking_edit_draft/show.html.twig', [
'draft' => $draft,
'returnUrl' => $returnUrl,
]);
}
#[Route('/admin/booking-edit-draft/{id}/delete', name: 'app_admin_bookingeditdraft_delete')]
public function delete(BookingEditDraft $draft, Request $request): Response
{
if (true === $request->isMethod(Request::METHOD_POST)) {
$this->entityManager->remove($draft);
$this->entityManager->flush();
$this->adminLogger->info('Delete booking edit draft', [
'booking_number' => $draft->getBookingNumber(),
]);
$this->addFlash('success', 'Der Buchungsentwurf wurde gelöscht');
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
return new HxRedirectResponse($returnUrl);
}
return $this->render('admin/booking_edit_draft/modal_delete.html.twig', [
'draft' => $draft,
]);
}
#[Route('/admin/booking-edit-draft/{id}/export', name: 'app_admin_bookingeditdraft_export')]
public function export(BookingEditDraft $draft): Response
{
if (false === $draft->hasExportData()) {
$this->addFlash('warning', 'Der Export ist fehlgeschlagen');
return $this->redirectToRoute('app_admin_bookingeditdraft');
}
try {
return $this->bookingExporter->createExportResponse($draft);
} catch (\RuntimeException $e) {
$this->addFlash('warning', 'Der Export ist fehlgeschlagen: '.$e->getMessage());
return $this->redirectToRoute('app_admin_bookingeditdraft');
}
}
}
@@ -4,10 +4,13 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\Voter\AdministrativeAccessVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted(AdministrativeAccessVoter::ADMINISTRATIVE_ACCESS)]
class DashboardController extends AbstractController
{
#[Route('/admin/dashboard', name: 'app_admin_dashboard')]
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Log;
use App\Service\XmlDumpReader;
use League\Flysystem\FilesystemException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_ADMIN')]
class DownloadController extends AbstractController
{
public function __construct(private readonly XmlDumpReader $xmlDumpReader)
{
}
#[Route('/admin/log/download/{filename}', name: 'app_admin_log_xmldump_download', requirements: ['filename' => '.+'])]
public function index(string $filename): Response
{
try {
if (false === $this->xmlDumpReader->fileExists($filename)) {
throw $this->createNotFoundException('Dump file not found. It may have been cleaned up.');
}
$content = $this->xmlDumpReader->getContent($filename);
} catch (FilesystemException $e) {
throw $this->createNotFoundException('Failed to read dump file: '.$e->getMessage());
}
$response = new Response($content);
$response->headers->set('Content-Type', 'application/xml');
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
basename($filename)
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Log;
use App\Repository\LogEntryRepository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_ADMIN')]
class IndexController extends AbstractController
{
public function __construct(
private readonly LogEntryRepository $logEntryRepository,
private readonly PaginatorInterface $paginator,
) {
}
#[Route('/admin/log', name: 'app_admin_log')]
public function index(Request $request): Response
{
$qb = $this
->logEntryRepository
->createQueryBuilder('log_entry')
;
$pagination = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 50),
[
'defaultSortFieldName' => 'log_entry.createdAt',
'defaultSortDirection' => 'DESC',
]
);
return $this->render('admin/log/index.html.twig', [
'pagination' => $pagination,
]);
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\Log;
use App\Entity\LogEntry;
use App\Service\XmlDumpReader;
use League\Flysystem\FilesystemException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_ADMIN')]
class XmlDumpController extends AbstractController
{
public function __construct(private readonly XmlDumpReader $xmlDumpReader)
{
}
#[Route('/admin/log/{id}/xml-dumps', name: 'app_admin_log_xmldumps')]
public function index(LogEntry $logEntry): Response
{
$dumps = [];
try {
$dumps = $this->xmlDumpReader->findDumpsForRequestId($logEntry->getRequestId());
} catch (FilesystemException) {
}
return $this->render('admin/log/xml_dumps.html.twig', [
'logEntry' => $logEntry,
'dumps' => $dumps,
]);
}
}
-90
View File
@@ -1,90 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Log;
use App\Entity\LogEntry;
use App\Repository\LogEntryRepository;
use App\Service\XmlDumpReader;
use Knp\Component\Pager\PaginatorInterface;
use League\Flysystem\FilesystemException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Attribute\Route;
class LogController extends AbstractController
{
public function __construct(
private readonly LogEntryRepository $logEntryRepository,
private readonly PaginatorInterface $paginator,
private readonly XmlDumpReader $xmlDumpReader,
) {
}
#[Route('/admin/log', name: 'app_admin_log')]
public function index(Request $request): Response
{
$qb = $this
->logEntryRepository
->createQueryBuilder('log_entry')
;
$pagination = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 50),
[
'defaultSortFieldName' => 'log_entry.createdAt',
'defaultSortDirection' => 'DESC',
]
);
return $this->render('admin/log/index.html.twig', [
'pagination' => $pagination,
]);
}
#[Route('/admin/log/{id}/xml-dumps', name: 'app_admin_log_xmldumps')]
public function xmlDumps(LogEntry $logEntry): Response
{
$dumps = [];
try {
$dumps = $this->xmlDumpReader->findDumpsForRequestId($logEntry->getRequestId());
} catch (FilesystemException) {
}
return $this->render('admin/log/xml_dumps.html.twig', [
'logEntry' => $logEntry,
'dumps' => $dumps,
]);
}
#[Route('/admin/log/download/{filename}', name: 'app_admin_log_xmldump_download', requirements: ['filename' => '.+'])]
public function downloadXmlDump(string $filename): Response
{
try {
if (false === $this->xmlDumpReader->fileExists($filename)) {
throw $this->createNotFoundException('Dump file not found. It may have been cleaned up.');
}
$content = $this->xmlDumpReader->getContent($filename);
} catch (FilesystemException $e) {
throw $this->createNotFoundException('Failed to read dump file: '.$e->getMessage());
}
$response = new Response($content);
$response->headers->set('Content-Type', 'application/xml');
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
basename($filename)
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Controller\Admin;
namespace App\Controller\Admin\User;
use App\Repository\UserRepository;
use Knp\Component\Pager\PaginatorInterface;
@@ -10,8 +10,10 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class UserController extends AbstractController
#[IsGranted('ROLE_ADMIN')]
class IndexController extends AbstractController
{
public function __construct(
private readonly UserRepository $userRepository,