Admin Panel Improvements:

- Updated custom admin controllers to EasyAdmin AdminRoutes.
- Added Admin error page.
- Various fixes and improvements.
This commit is contained in:
2025-12-08 20:57:27 +01:00
parent fe2bc7b78c
commit 2adaffeba9
17 changed files with 329 additions and 231 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ docker run -d --name somniarooms-db --hostname somniadb -p 5432:5432 --env=POSTG
Then SomniaRooms can be deployed on a container (change --env-file value to the created file):
```bash
docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./env.prod.local somniarooms:prod
docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./.env.prod.local --network somniarooms-network somniarooms:prod
```
When deploying to prod is recomended to mount some directories as a docker volume or hostpath. This makes sure data is preserved after deleting the container.
@@ -2,39 +2,30 @@
namespace App\Controller\Admin;
use App\Service\AdminUtils;
use App\Service\FileUploaderService;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Validator\Constraints\File;
#[IsGranted("ROLE_ADMIN")]
class PhotoManagerAdminController extends AbstractController
{
private AdminUrlGenerator $adminUrlGenerator;
private FileUploaderService $fileUploader;
public function __construct(
AdminUrlGenerator $adminUrlGenerator,
FileUploaderService $fu
) {
$this->adminUrlGenerator = $adminUrlGenerator;
$this->fileUploader = $fu;
}
private FileUploaderService $fileUploader
) {}
#[Route('/admin/photos', name: 'admin_photos')]
#[AdminRoute('/photos', name: 'photos')]
public function index(): Response
{
$fileData = [];
@@ -57,7 +48,7 @@ class PhotoManagerAdminController extends AbstractController
]);
}
#[Route('/admin/photos/upload', name: 'admin_photos_upload')]
#[AdminRoute('/photos/upload', name: 'photos_upload')]
public function uploadPhoto(
Request $request
): Response {
@@ -67,16 +58,17 @@ class PhotoManagerAdminController extends AbstractController
'mapped' => false,
'required' => false,
'constraints' => [
new File([
'maxSize' => '2048k',
'mimeTypes' => [
new File(
maxSize: '2048k',
maxSizeMessage: 'Selected image is too big!',
mimeTypes: [
'image/jpeg',
'image/png',
'image/svg+xml',
'image/webp',
],
'mimeTypesMessage' => 'Please upload a valid image!',
])
mimeTypesMessage: 'Please upload a valid image!'
)
],
])
->add('submit', SubmitType::class)
@@ -92,8 +84,8 @@ class PhotoManagerAdminController extends AbstractController
$filename = $this->fileUploader->upload($uploadedPhoto);
}
$url = $this->adminUrlGenerator->setRoute('admin_photos')->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Photo uploaded!');
return $this->redirectToRoute(AdminUtils::GetAdminRoute('photos'));
}
return $this->render('admin/adminEdit.html.twig', [
@@ -103,7 +95,7 @@ class PhotoManagerAdminController extends AbstractController
]);
}
#[Route('/admin/photos/remove/{filename}', name: 'admin_photos_remove')]
#[AdminRoute('/photos/remove/{filename}', name: 'photos_remove')]
public function remove(string $filename): Response
{
$filePath = $this->fileUploader->getTargetDirectory() . $filename;
@@ -113,7 +105,7 @@ class PhotoManagerAdminController extends AbstractController
throw new HttpException("404", "File can't be deleted");
}
$url = $this->adminUrlGenerator->setRoute('admin_photos')->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Photo deleted!');
return $this->redirectToRoute(AdminUtils::GetAdminRoute('photos'));
}
}
@@ -2,48 +2,42 @@
namespace App\Controller\Admin;
use App\Entity\RoomAvailability;
use App\Entity\RoomCategory;
use App\Enum\AdminErrorEnum;
use App\Repository\RoomAvailabilityRepository;
use App\Repository\RoomCategoryRepository;
use App\Service\AdminUtils;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
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 RoomAvailabilityAdminController extends AbstractController
{
private $adminUrlGenerator;
private EntityManagerInterface $entityManager;
public const string ROUTE_INDEX = 'roomAvailability_category';
public const string ROUTE_BULKEDIT = 'roomAvailability_category_bulkupdate';
public function __construct(AdminUrlGenerator $adminUrlGenerator, EntityManagerInterface $em)
{
$this->adminUrlGenerator = $adminUrlGenerator;
$this->entityManager = $em;
}
#[Route('/admin/roomAvailability', name: 'admin_roomAvailability')]
public function index(RoomAvailabilityRepository $repo, RoomCategoryRepository $catRepo): Response
{
$categoryList = $catRepo->findAll();
return $this->render('admin/room_availability/index.html.twig', [
'roomCategoryList' => $categoryList,
]);
}
public function __construct(
private EntityManagerInterface $entityManager,
private AdminUtils $adminUtils,
) {}
// List RoomAvailability items from category.
#[Route('/admin/roomAvailability/{categoryId}', name: 'admin_roomAvailability_category')]
#[AdminRoute('/roomAvailability/{categoryId}', name: RoomAvailabilityAdminController::ROUTE_INDEX)]
public function categoryAvailability(RoomAvailabilityRepository $repo, RoomCategoryRepository $catRepo, string $categoryId): Response
{
$category = $catRepo->find($categoryId);
if ($category == null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$availabilityList = $repo->findByRoomCategory($categoryId);
return $this->render('admin/room_availability/category_view.html.twig', [
@@ -53,18 +47,12 @@ class RoomAvailabilityAdminController extends AbstractController
}
// Edit a RoomAvailability numAvailable.
#[Route('/admin/roomAvailability/edit/{categoryId}/{day}', name: 'admin_roomAvailability_category_update')]
#[AdminRoute('/roomAvailability/edit/{categoryId}/{day}', name: 'roomAvailability_category_update')]
public function updateCategoryAvailability(Request $request, RoomAvailabilityRepository $repo, string $categoryId, string $day): Response
{
$availabilityItem = $repo->find(["day" => $day, "roomCategory" => $categoryId]);
if ($availabilityItem == null) {
//TODOME: Aquí deberíamos llevar a una página de error o algo.
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability_category', [
'categoryId' => $categoryId,
])->generateUrl();
return $this->redirect($url);
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$form = $this->createFormBuilder($availabilityItem)
@@ -79,11 +67,11 @@ class RoomAvailabilityAdminController extends AbstractController
$this->entityManager->flush();
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability_category', [
'categoryId' => $categoryId,
])->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Availability Updated!');
return $this->redirectToRoute(
AdminUtils::GetAdminRoute(RoomAvailabilityAdminController::ROUTE_INDEX),
['categoryId' => $categoryId,]
);
}
return $this->render('admin/room_availability/availability_day_edit.html.twig', [
@@ -93,9 +81,13 @@ class RoomAvailabilityAdminController extends AbstractController
}
// Bulk edit all roomAvailability of a category on period.
#[Route('/admin/roomAvailability/{categoryId}/bulkedit', name: 'admin_roomAvailability_category_bulkupdate')]
#[AdminRoute('/roomAvailability/{categoryId}/bulkedit', name: RoomAvailabilityAdminController::ROUTE_BULKEDIT)]
public function bulkUpdateCategoryAvailability(Request $request, RoomAvailabilityRepository $repo, string $categoryId): Response
{
$categoryItem = $this->entityManager->find(RoomCategory::class, $categoryId);
if ($categoryItem == null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$form = $this->createFormBuilder()
->add('startDate', DateType::class, [
@@ -114,18 +106,15 @@ class RoomAvailabilityAdminController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) {
// $form->getData() holds the submitted values
$data = $form->getData();
$categoryItem = $this->entityManager->find(RoomCategory::class, $categoryId);
$repo->bulkEditRoomAvailabilityForCategoryOnPeriod($categoryItem, $data['startDate'], $data['endDate'], $data['numAvailable']);
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability')->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Period Availability Updated!');
return $this->redirectToRoute(AdminUtils::GetAdminRoute('room_availability_index'));
}
return $this->render('admin/room_availability/availability_bulk_edit.html.twig', [
'form' => $form,
'categoryId' => $categoryId,
'category' => $categoryItem,
]);
}
}
@@ -0,0 +1,62 @@
<?php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\{TextField};
use App\Entity\RoomCategory;
use App\Service\AdminUtils;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
class RoomAvailabilityCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
//Availability is configured by RoomCategory.
return RoomCategory::class;
}
public function configureCrud(Crud $crud): Crud
{
return $crud
->setPageTitle(Crud::PAGE_INDEX, "Room Category Availability")
->setHelp(Crud::PAGE_INDEX, "Edit Availability for a RoomCategory.")
->showEntityActionsInlined();
}
public function configureFields(string $pageName): iterable
{
//$catRepo = $this->entityManager->getRepository(SomeEntity::class);
yield TextField::new('name');
yield TextField::new('description');
}
public function configureActions(Actions $actions): Actions
{
$actions->disable(Action::NEW, Action::DETAIL, Action::EDIT, Action::DELETE);
$viewAvailabilityAction = Action::new('viewAvailability', 'View Availability')
->linkToRoute(
AdminUtils::GetAdminRoute(RoomAvailabilityAdminController::ROUTE_INDEX),
function (RoomCategory $categoryItem): array {
return ['categoryId' => $categoryItem->getId()];
}
);
$updatePeriodAction = Action::new("updatePeriod", "Update Period")
->linkToRoute(
AdminUtils::GetAdminRoute(RoomAvailabilityAdminController::ROUTE_BULKEDIT),
function (RoomCategory $categoryItem): array {
return ['categoryId' => $categoryItem->getId()];
}
);
return $actions
->add(Crud::PAGE_INDEX, $viewAvailabilityAction)
->add(Crud::PAGE_INDEX, $updatePeriodAction);
}
}
@@ -4,58 +4,55 @@ namespace App\Controller\Admin;
use App\Entity\RoomCategory;
use App\Entity\RoomCategoryBedType;
use App\Enum\AdminErrorEnum;
use App\Repository\RoomCategoryRepository;
use App\Repository\RoomCategoryBedTypeRepository;
use App\Form\RoomCategoryBedTypeFormType;
use App\Service\AdminUtils;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
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 RoomCategoryBedTypeAdminController extends AbstractController
{
private $adminUrlGenerator;
private EntityManagerInterface $entityManager;
public const string ROUTE_INDEX = 'roomCategoryBedType_category';
public function __construct(AdminUrlGenerator $adminUrlGenerator, EntityManagerInterface $em)
{
$this->adminUrlGenerator = $adminUrlGenerator;
$this->entityManager = $em;
}
#[Route('/admin/roomCategoryBedType', name: 'admin_roomCategoryBedType')]
public function index(RoomCategoryRepository $catRepo): Response
{
$categoryList = $catRepo->findAll();
return $this->render('admin/room_category_bed_type_admin/index.html.twig', [
'roomCategoryList' => $categoryList,
]);
}
public function __construct(
private EntityManagerInterface $entityManager,
private AdminUtils $adminUtils
) {}
// List BedTypes from category.
#[Route('/admin/roomCategoryBedType/{categoryId}', name: 'admin_roomCategoryBedType_category')]
#[AdminRoute('/roomCategoryBedType/{categoryId}', name: RoomCategoryBedTypeAdminController::ROUTE_INDEX)]
public function categoryBedTypes(RoomCategoryRepository $catRepo, RoomCategoryBedTypeRepository $repo, string $categoryId): Response
{
$category = $catRepo->find($categoryId);
$roomCategoryItem = $catRepo->find($categoryId);
if ($roomCategoryItem === null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$bedTypeList = $repo->findByRoomCategory($categoryId);
return $this->render('admin/room_category_bed_type_admin/categoryBedTypes.html.twig', [
'categoryData' => $category,
'categoryData' => $roomCategoryItem,
'bedTypeList' => $bedTypeList,
]);
}
// Create a RoomCategory BedType.
#[Route('/admin/roomCategoryBedType/{categoryId}/new', name: 'admin_roomCategoryBedType_category_create')]
#[AdminRoute('/roomCategoryBedType/{categoryId}/new', name: 'roomCategoryBedType_category_create')]
public function createCategoryBedType(Request $request, RoomCategoryBedTypeRepository $repo, string $categoryId): Response
{
$roomCatBedType = new RoomCategoryBedType();
$roomCategoryItem = $this->entityManager->find(RoomCategory::class, $categoryId);
if ($roomCategoryItem === null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$roomCatBedType = new RoomCategoryBedType();
$roomCatBedType->setRoomCategory($roomCategoryItem);
$form = $this->createForm(RoomCategoryBedTypeFormType::class, $roomCatBedType, [
@@ -67,14 +64,13 @@ class RoomCategoryBedTypeAdminController extends AbstractController
// $form->getData() holds the submitted values
// but, the original `$availabilityItem` variable has also been updated
//TODO: Validate if selected BedType already exist for current RoomCategory.
$this->entityManager->persist($roomCatBedType);
$this->entityManager->flush();
$url = $this->adminUrlGenerator->setRoute('admin_roomCategoryBedType_category', [
'categoryId' => $roomCatBedType->getRoomCategory()->getId(),
])->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Element created!');
return $this->goToIndex($categoryId);
}
return $this->render('admin/adminEdit.html.twig', [
@@ -85,18 +81,13 @@ class RoomCategoryBedTypeAdminController extends AbstractController
}
// Edit a RoomCategory BedType.
#[Route('/admin/roomCategoryBedType/{categoryId}/{bedTypeId}/edit', name: 'admin_roomCategoryBedType_category_update')]
#[AdminRoute('/roomCategoryBedType/{categoryId}/{bedTypeId}/edit', name: 'roomCategoryBedType_category_update')]
public function updateCategoryBedType(Request $request, RoomCategoryBedTypeRepository $repo, string $categoryId, string $bedTypeId): Response
{
$categoryBedTypeItem = $repo->find(["bedType" => $bedTypeId, "roomCategory" => $categoryId]);
if ($categoryBedTypeItem == null) {
//TODOME: Aquí deberíamos llevar a una página de error o algo.
$url = $this->adminUrlGenerator->setRoute('admin_roomCategoryBedType_category', [
'categoryId' => $categoryId,
])->generateUrl();
return $this->redirect($url);
if ($categoryBedTypeItem === null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$form = $this->createForm(RoomCategoryBedTypeFormType::class, $categoryBedTypeItem, [
@@ -110,11 +101,8 @@ class RoomCategoryBedTypeAdminController extends AbstractController
$this->entityManager->flush();
$url = $this->adminUrlGenerator->setRoute('admin_roomCategoryBedType_category', [
'categoryId' => $categoryId,
])->generateUrl();
return $this->redirect($url);
$this->addFlash('primary', 'Element updated!');
return $this->goToIndex($categoryId);
}
return $this->render('admin/adminEdit.html.twig', [
@@ -125,19 +113,25 @@ class RoomCategoryBedTypeAdminController extends AbstractController
}
// Remove a RoomCategoryBedType.
#[Route('/admin/roomCategoryBedType/{categoryId}/{bedTypeId}/remove', name: 'admin_roomCategoryBedType_category_remove')]
public function removeCategoryBedType(RoomCategoryBedTypeRepository $repo, string $categoryId, string $bedTypeId) : Response {
#[AdminRoute('/roomCategoryBedType/{categoryId}/{bedTypeId}/remove', name: 'roomCategoryBedType_category_remove')]
public function removeCategoryBedType(RoomCategoryBedTypeRepository $repo, string $categoryId, string $bedTypeId): Response
{
$categoryBedTypeItem = $repo->find(["bedType" => $bedTypeId, "roomCategory" => $categoryId]);
if(!$categoryBedTypeItem) {
throw $this->createNotFoundException('Element not found');
if ($categoryBedTypeItem === null) {
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
}
$this->entityManager->remove($categoryBedTypeItem);
$this->entityManager->flush();
$url = $this->adminUrlGenerator->setRoute('admin_roomCategoryBedType_category', [
'categoryId' => $categoryId,
])->generateUrl();
return $this->redirect($url);
$this->addFlash('success', 'Element deleted!');
return $this->goToIndex($categoryId);
}
private function goToIndex(int $roomCategory): Response
{
return $this->redirectToRoute(AdminUtils::GetAdminRoute(RoomCategoryBedTypeAdminController::ROUTE_INDEX), [
'categoryId' => $roomCategory,
]);
}
}
@@ -2,19 +2,12 @@
namespace App\Controller\Admin;
use App\Entity\RoomCategoryBedType;
use App\Form\RoomCategoryBedTypeForm;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\Page;
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
use EasyCorp\Bundle\EasyAdminBundle\Field\CollectionField;
use EasyCorp\Bundle\EasyAdminBundle\Field\{TextField, IntegerField, CollectionField};
use EasyCorp\Bundle\EasyAdminBundle\Config\{Actions, Action};
use App\Entity\RoomCategory;
use App\Service\AdminUtils;
class RoomCategoryCrudController extends AbstractCrudController
{
@@ -36,4 +29,19 @@ class RoomCategoryCrudController extends AbstractCrudController
yield IntegerField::new('orderHomePage');
yield CollectionField::new('roomCategoryPhotos')->useEntryCrudForm();
}
public function configureActions(Actions $actions): Actions
{
$viewBedTypes = Action::new('bedTypes', 'Bed Types')
->setIcon('fas fa-bed')
->linkToRoute(
AdminUtils::GetAdminRoute(RoomCategoryBedTypeAdminController::ROUTE_INDEX),
function (RoomCategory $categoryItem): array {
return ['categoryId' => $categoryItem->getId()];
}
);
return $actions
->add(Crud::PAGE_INDEX, $viewBedTypes);
}
}
@@ -14,7 +14,6 @@ use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class SomniaDashboardController extends AbstractDashboardController
@@ -26,7 +25,7 @@ class SomniaDashboardController extends AbstractDashboardController
public function configureDashboard(): Dashboard
{
return Dashboard::new ()
return Dashboard::new()
->setTitle('SomniaRooms');
}
@@ -38,14 +37,13 @@ class SomniaDashboardController extends AbstractDashboardController
yield MenuItem::linkToCrud('Bookings', 'fas fa-book', Booking::class);
yield MenuItem::Section('Room Management');
yield MenuItem::linkToCrud('Category', 'fas fa-layer-group', RoomCategory::class);
yield MenuItem::linkToRoute('Room Availability', 'fas fa-calendar-check', 'admin_roomAvailability');
yield MenuItem::linkToCrud('Availability', 'fas fa-calendar-check', RoomCategory::class)->setController(RoomAvailabilityCrudController::class);
yield MenuItem::linkToCrud('Fare Tables', 'fas fa-table', FareTable::class);
yield MenuItem::linkToCrud('Room Fares', 'fas fa-tag', RoomFare::class);
yield MenuItem::Section('Category Configuration');
yield MenuItem::linkToCrud('Category', 'fas fa-layer-group', RoomCategory::class)->setController(RoomCategoryCrudController::class);
yield MenuItem::linkToCrud('Bed Types', 'fas fa-bed', BedType::class);
yield MenuItem::linkToRoute('Bed Type - category', 'fas fa-bed', 'admin_roomCategoryBedType');
yield MenuItem::Section('Resources');
yield MenuItem::linkToRoute('Photos', 'fas fa-image', 'admin_photos');
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Enum;
enum AdminErrorEnum
{
case ENTITY_NOTFOUND;
case UNKNOW_ERROR;
public function getTitle(): string
{
return match ($this) {
AdminErrorEnum::ENTITY_NOTFOUND => 'Not Found!',
AdminErrorEnum::UNKNOW_ERROR => "Unknown Error!",
};
}
public function getMsg(): string
{
return match ($this) {
AdminErrorEnum::ENTITY_NOTFOUND => 'Entity not found!',
AdminErrorEnum::UNKNOW_ERROR => "There have been an unknown error. Try again.",
};
}
public function getHttpCode(): int
{
return match ($this) {
AdminErrorEnum::ENTITY_NOTFOUND => 404,
AdminErrorEnum::UNKNOW_ERROR => 500,
};
}
}
@@ -21,6 +21,12 @@ class ApiExceptionSubscriber implements EventSubscriberInterface
public function onKernelException(ExceptionEvent $event): void
{
$req = $event->getRequest();
if (str_contains($req->getPathInfo(), 'admin')) {
//Todo: In admin Panel go to error page with "Unknown Error".
return;
}
$e = $event->getThrowable();
if ($e instanceof ApiErrorException) {
$apiErrorMsg = $e->getApiErrorMsg();
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Service;
use App\Enum\AdminErrorEnum;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;
class AdminUtils
{
const string AdminRoutePrefix = "admin";
public function __construct(
private LoggerInterface $logger,
private Environment $twig,
) {}
public function goToAdminError(string $errorTitle, string $errorMsg, int $httpCode = 500): Response
{
$error = ["title" => $errorTitle, "message" => $errorMsg];
$htmlContents = $this->twig->render('admin/adminError.html.twig', [
"error" => $error,
]);
return new Response($htmlContents, $httpCode);
}
public function goToAdminErrorType(AdminErrorEnum $errorType)
{
return $this->goToAdminError($errorType->getTitle(), $errorType->getMsg(), $errorType->getHttpCode());
}
public static function GetAdminRoute(string $adminRouteName): string
{
return AdminUtils::AdminRoutePrefix . '_' . $adminRouteName;
}
}
+17
View File
@@ -0,0 +1,17 @@
{# templates/admin/adminEdit.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block body_class 'error' %}
{% block page_title %}{{ error.title }}{% endblock %}
{% block content_title error.title %}
{% block content_header_wrapper '' %}
{% block content_footer_wrapper '' %}
{% block main %}
<div class="error-message">
<h1>
<twig:ea:Icon name="internal:circle-exclamation" /> {{ block('page_title') }}
</h1>
{{ error.message }}
</div>
{% endblock %}
+21 -7
View File
@@ -4,25 +4,39 @@
{% block content_title 'Photo Manager' %}
{% block main %}
<a class="btn btn-primary m-3" href="{{ ea_url().setRoute('admin_photos_upload') }}">
<a class="btn btn-primary m-3" href="{{ path('admin_photos_upload') }}">
Add new
</a>
</a>
<div style="display: flex; flex-direction: row; flex-wrap: wrap; gap: 18px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; gap: 18px;">
{% for photo in photoList %}
<div class="card text-center" style="width: 40%;">
<p>{{photo.filename}}</p>
<img src="{{ asset(photo.url) }}" style="max-width: 80%;"/>
<img src="{{ asset(photo.url) }}" style="max-width: 80%;" />
<br>
<a class="btn btn-outline-primary m-2">
<a class="btn btn-outline-primary m-2"
href="javascript:CopyImageLinkToClipboard('{{absolute_url(photo.url)}}')">
<i class="fas fa-clipboard">&nbsp;&nbsp;Copy!</i>
</a>
<a class="btn btn-primary m-2 mt-1" href="{{ ea_url().setRoute('admin_photos_remove', { filename: photo.filename }) }}">
<a class="btn btn-danger m-2 mt-1" href="{{ path('admin_photos_remove', { filename: photo.filename }) }}">
Delete
</a>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{% block body_javascript %}
<script>
async function CopyImageLinkToClipboard(imgLink) {
try {
await navigator.clipboard.writeText(imgLink);
} catch (error) {
console.error(error.message);
}
}
</script>
{% endblock body_javascript %}
@@ -1,10 +1,10 @@
{# templates/admin/room_availability/availability_day_edit.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title 'Category - Room Availability - Edit' %}
{% block content_title 'Category - Room Availability - Bulk Edit' %}
{% block main %}
<div>
<h3>{{categoryId}}</h3>
<h3>{{category.name}}</h3>
</div>
{{ form(form) }}
{% endblock %}
@@ -6,11 +6,12 @@
<div>
<h3>{{categoryData.name}}</h3>
</div>
<table class="datagrid">
<table class="table datagrid {{ availabilityList is empty ? 'datagrid-empty' }}">
<thead>
<tr>
<td>Day</td>
<td>Amount</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
@@ -19,7 +20,8 @@
<td>{{availability.day |date('d-m-Y')}}</td>
<td>{{availability.numAvailable}}</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomAvailability_category_update', { day: availability.day |date('Y-m-d'), categoryId: categoryData.id }) }}">
<a class="btn btn-secondary"
href="{{ path('admin_roomAvailability_category_update', { day: availability.day |date('Y-m-d'), categoryId: categoryData.id }) }}">
Edit
</a>
</td>
@@ -1,34 +0,0 @@
{# templates/admin/room_availability/index.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title 'Room Availability' %}
{% block main %}
<table class="datagrid">
<thead>
<tr>
<td>ID</td>
<td>Category</td>
<td>Details</td>
<td>Period update</td>
</tr>
</thead>
<tbody>
{% for category in roomCategoryList %}
<tr>
<td>{{category.id}}</td>
<td>{{category.name}}</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomAvailability_category', { categoryId: category.id }) }}">
View Details
</a>
</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomAvailability_category_bulkupdate', { categoryId: category.id }) }}">
Update Period
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
@@ -1,21 +1,25 @@
{# templates/admin/room_category_bed_type_admin/category_view.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title 'Room Availability' %}
{% block content_title 'Room Bed Types' %}
{% block main %}
<div>
<div class="row justify-content-between mb-4">
<div class="col">
<h3>{{categoryData.name}}</h3>
</div>
<div>
<a href="{{ ea_url().setRoute('admin_roomCategoryBedType_category_create', { categoryId: categoryData.id }) }}">
</div>
<div class="col">
<a class="btn btn-primary"
href="{{ path('admin_roomCategoryBedType_category_create', { categoryId: categoryData.id }) }}">
Add BedType
</a>
</div>
</div>
<table class="datagrid">
<table class="table datagrid {{ bedTypeList is empty ? 'datagrid-empty' }}">
<thead>
<tr>
<td>BedType</td>
<td>Amount</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
@@ -24,12 +28,12 @@
<td>{{bedType.bedType}}</td>
<td>{{bedType.amount}}</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomCategoryBedType_category_update', { bedTypeId: bedType.bedType.id, categoryId: bedType.roomCategory.id }) }}">
<a class="btn btn-secondary"
href="{{ path('admin_roomCategoryBedType_category_update', { bedTypeId: bedType.bedType.id, categoryId: bedType.roomCategory.id }) }}">
Edit
</a>
</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomCategoryBedType_category_remove', { bedTypeId: bedType.bedType.id, categoryId: bedType.roomCategory.id }) }}">
<a class="btn btn-danger"
href="{{ path('admin_roomCategoryBedType_category_remove', { bedTypeId: bedType.bedType.id, categoryId: bedType.roomCategory.id }) }}">
Remove
</a>
</td>
@@ -1,27 +0,0 @@
{# templates/admin/room_category_bed_type_admin/index.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}
{% block content_title 'Category Bed Type' %}
{% block main %}
<table class="datagrid">
<thead>
<tr>
<td>ID</td>
<td>RoomCategory</td>
</tr>
</thead>
<tbody>
{% for category in roomCategoryList %}
<tr>
<td>{{category.id}}</td>
<td>{{category.name}}</td>
<td>
<a href="{{ ea_url().setRoute('admin_roomCategoryBedType_category', { categoryId: category.id }) }}">
View Details
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}