diff --git a/migrations/Version20260320214602.php b/migrations/Version20260320214602.php new file mode 100644 index 0000000..8b868e1 --- /dev/null +++ b/migrations/Version20260320214602.php @@ -0,0 +1,34 @@ +addSql('CREATE TABLE category (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, position INT NOT NULL, start_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, end_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, event_id INT NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE INDEX IDX_64C19C171F7E88B ON category (event_id)'); + $this->addSql('ALTER TABLE category ADD CONSTRAINT FK_64C19C171F7E88B FOREIGN KEY (event_id) REFERENCES event (id) ON DELETE CASCADE NOT DEFERRABLE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE category DROP CONSTRAINT FK_64C19C171F7E88B'); + $this->addSql('DROP TABLE category'); + } +} diff --git a/phpstan.neon b/phpstan.neon index c21a29c..67d9597 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,10 +6,11 @@ parameters: - src/Kernel.php ignoreErrors: - - message: '#Property App\\Entity\\(EmailTracking|MessengerLog|User|Payout|Event)::\$id .* never assigned#' + message: '#Property App\\Entity\\(EmailTracking|MessengerLog|User|Payout|Event|Category)::\$id .* never assigned#' paths: - src/Entity/EmailTracking.php - src/Entity/MessengerLog.php - src/Entity/User.php - src/Entity/Payout.php - src/Entity/Event.php + - src/Entity/Category.php diff --git a/src/Controller/AccountController.php b/src/Controller/AccountController.php index 48b6f57..e0acb2e 100644 --- a/src/Controller/AccountController.php +++ b/src/Controller/AccountController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use App\Entity\Category; use App\Entity\Event; use App\Entity\Payout; use App\Entity\User; @@ -365,16 +366,111 @@ class AccountController extends AbstractController return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId()]); } + $categories = $em->getRepository(Category::class)->findBy( + ['event' => $event], + ['position' => 'ASC'], + ); + return $this->render('account/edit_event.html.twig', [ 'event' => $event, + 'categories' => $categories, 'breadcrumbs' => [ self::BREADCRUMB_HOME, self::BREADCRUMB_ACCOUNT, - ['name' => 'Modifier un evenement', 'url' => '/mon-compte/evenement/'.$event->getId().'/modifier'], + ['name' => $event->getTitle(), 'url' => '/mon-compte/evenement/'.$event->getId().'/modifier'], ], ]); } + #[Route('/mon-compte/evenement/{id}/categorie/ajouter', name: 'app_account_event_add_category', methods: ['POST'])] + public function addCategory(Event $event, Request $request, EntityManagerInterface $em): Response + { + $this->denyAccessUnlessGranted('ROLE_ORGANIZER'); + + /** @var User $user */ + $user = $this->getUser(); + if ($event->getAccount()->getId() !== $user->getId()) { + throw $this->createAccessDeniedException(); + } + + $name = trim($request->request->getString('name')); + if ('' === $name) { + $this->addFlash('error', 'Le nom de la categorie est requis.'); + + return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId(), 'tab' => 'categories']); + } + + $maxPosition = $em->getRepository(Category::class)->count(['event' => $event]); + + $category = new Category(); + $category->setName($name); + $category->setEvent($event); + $category->setPosition($maxPosition); + + $startAt = $request->request->getString('start_at'); + if ('' !== $startAt) { + $category->setStartAt(new \DateTimeImmutable($startAt)); + } + + $endAt = $request->request->getString('end_at'); + if ('' !== $endAt) { + $category->setEndAt(new \DateTimeImmutable($endAt)); + } + + $em->persist($category); + $em->flush(); + + $this->addFlash('success', sprintf('Categorie "%s" ajoutee.', $name)); + + return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId(), 'tab' => 'categories']); + } + + #[Route('/mon-compte/evenement/{id}/categorie/{categoryId}/supprimer', name: 'app_account_event_delete_category', methods: ['POST'])] + public function deleteCategory(Event $event, int $categoryId, EntityManagerInterface $em): Response + { + $this->denyAccessUnlessGranted('ROLE_ORGANIZER'); + + /** @var User $user */ + $user = $this->getUser(); + if ($event->getAccount()->getId() !== $user->getId()) { + throw $this->createAccessDeniedException(); + } + + $category = $em->getRepository(Category::class)->find($categoryId); + if ($category && $category->getEvent()->getId() === $event->getId()) { + $em->remove($category); + $em->flush(); + $this->addFlash('success', 'Categorie supprimee.'); + } + + return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId(), 'tab' => 'categories']); + } + + #[Route('/mon-compte/evenement/{id}/categorie/reorder', name: 'app_account_event_reorder_categories', methods: ['POST'])] + public function reorderCategories(Event $event, Request $request, EntityManagerInterface $em): Response + { + $this->denyAccessUnlessGranted('ROLE_ORGANIZER'); + + /** @var User $user */ + $user = $this->getUser(); + if ($event->getAccount()->getId() !== $user->getId()) { + throw $this->createAccessDeniedException(); + } + + $order = json_decode($request->getContent(), true); + if (\is_array($order)) { + foreach ($order as $position => $categoryId) { + $category = $em->getRepository(Category::class)->find($categoryId); + if ($category && $category->getEvent()->getId() === $event->getId()) { + $category->setPosition($position); + } + } + $em->flush(); + } + + return $this->json(['success' => true]); + } + #[Route('/mon-compte/evenement/{id}/en-ligne', name: 'app_account_toggle_event_online', methods: ['POST'])] public function toggleEventOnline(Event $event, EntityManagerInterface $em, EventIndexService $eventIndex): Response { diff --git a/src/Entity/Category.php b/src/Entity/Category.php new file mode 100644 index 0000000..ad6ca9f --- /dev/null +++ b/src/Entity/Category.php @@ -0,0 +1,122 @@ +createdAt = new \DateTimeImmutable(); + $this->startAt = new \DateTimeImmutable(); + $this->endAt = new \DateTimeImmutable('+30 days'); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getPosition(): int + { + return $this->position; + } + + public function setPosition(int $position): static + { + $this->position = $position; + + return $this; + } + + public function getEvent(): ?Event + { + return $this->event; + } + + public function setEvent(?Event $event): static + { + $this->event = $event; + + if ($event && $event->getStartAt()) { + $this->endAt = $event->getStartAt()->modify('-1 day'); + } + + return $this; + } + + public function getStartAt(): \DateTimeImmutable + { + return $this->startAt; + } + + public function setStartAt(\DateTimeImmutable $startAt): static + { + $this->startAt = $startAt; + + return $this; + } + + public function getEndAt(): \DateTimeImmutable + { + return $this->endAt; + } + + public function setEndAt(\DateTimeImmutable $endAt): static + { + $this->endAt = $endAt; + + return $this; + } + + public function getCreatedAt(): \DateTimeImmutable + { + return $this->createdAt; + } + + public function isActive(): bool + { + $now = new \DateTimeImmutable(); + + return $now >= $this->startAt && $now <= $this->endAt; + } +} diff --git a/src/Repository/CategoryRepository.php b/src/Repository/CategoryRepository.php new file mode 100644 index 0000000..77c5268 --- /dev/null +++ b/src/Repository/CategoryRepository.php @@ -0,0 +1,18 @@ + + */ +class CategoryRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Category::class); + } +} diff --git a/templates/account/edit_event.html.twig b/templates/account/edit_event.html.twig index 26ecf48..3d476d8 100644 --- a/templates/account/edit_event.html.twig +++ b/templates/account/edit_event.html.twig @@ -1,6 +1,6 @@ {% extends 'base.html.twig' %} -{% block title %}Modifier un evenement - E-Ticket{% endblock %} +{% block title %}{{ event.title }} - E-Ticket{% endblock %} {% block body %}
@@ -9,8 +9,8 @@ Retour aux evenements -

Modifier un evenement

-

Modifiez les informations de votre evenement.

+

{{ event.title }}

+

Gestion de l'evenement.

{% for message in app.flashes('success') %}

{{ message }}

@@ -19,7 +19,7 @@

{{ message }}

{% endfor %} -
+
{% if event.online %}
{% if event.online %} -
+

URL publique

{{ absolute_url(path('app_event_detail', {orgaSlug: event.account.slug, id: event.id, eventSlug: event.slug})) }}

@@ -74,9 +74,17 @@ Copier le lien
- {% endif %} + {% set current_tab = app.request.query.get('tab', 'info') %} + + + {% if current_tab == 'info' %}
@@ -149,5 +157,68 @@
+ + {% elseif current_tab == 'categories' %} +
+
+

Categories

+
+
+ +
+ + +
+
+ + +
+
+ + +
+ + + + {% if categories|length > 0 %} +
+ {% for category in categories %} +
+ + {{ category.name }} + {{ category.startAt|date('d/m/Y H:i') }} — {{ category.endAt|date('d/m/Y H:i') }} + {% if category.active %} + Active + {% else %} + Inactive + {% endif %} +
+ +
+
+ {% endfor %} +
+ {% else %} +

Aucune categorie. Ajoutez-en une pour commencer a vendre des billets.

+ {% endif %} +
+
+ + {% elseif current_tab == 'stats' %} +
+
+

Les statistiques seront disponibles prochainement.

+
+
+ + {% elseif current_tab == 'settings' %} +
+
+

Les parametres avances seront disponibles prochainement.

+
+
+ {% endif %}
{% endblock %} diff --git a/tests/Controller/AccountControllerTest.php b/tests/Controller/AccountControllerTest.php index bfddeda..6df2d6a 100644 --- a/tests/Controller/AccountControllerTest.php +++ b/tests/Controller/AccountControllerTest.php @@ -567,7 +567,7 @@ class AccountControllerTest extends WebTestCase 'is_online' => '1', ]); - self::assertResponseRedirects('/mon-compte?tab=events'); + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier'); } public function testEditEventDeniedForOtherUser(): void @@ -715,7 +715,7 @@ class AccountControllerTest extends WebTestCase 'city' => 'Paris', ], ['event_main_picture' => $picture]); - self::assertResponseRedirects('/mon-compte?tab=events'); + self::assertResponseRedirects(); } public function testEditEventWithPicture(): void @@ -755,7 +755,7 @@ class AccountControllerTest extends WebTestCase 'city' => 'Lyon', ], ['event_main_picture' => $picture]); - self::assertResponseRedirects('/mon-compte?tab=events'); + self::assertResponseRedirects(); } public function testEventsSearchReturnsSuccess(): void