Add Event entity, create event page, and custom WYSIWYG editor component

- Create Event entity with fields: account, title, description (text), startAt, endAt, address, zipcode, city, eventMainPicture (VichUploader), isOnline, createdAt, updatedAt
- Create EventRepository
- Add migration for event table with all columns
- Add "Creer un evenement" button on account events tab
- Add create event page (/mon-compte/evenement/creer) with full form
- Build custom web component <e-ticket-editor> WYSIWYG editor:
  - Toolbar: bold, italic, underline, paragraph, bullet list, remove formatting
  - contentEditable div with HTML sync to hidden textarea
  - HTML sanitizer (strips disallowed tags, XSS safe)
  - Neo-brutalist CSS styling
  - CSP compliant (no inline styles)
- Register editor in app.js via customElements.define
- Add editor CSS in app.scss
- Add 16 Event entity tests (all fields + isOnline + picture upload + updatedAt)
- Add 16 editor JS tests (sanitizer + custom element lifecycle)
- Add 3 AccountController tests (create event page, submit, access control)
- Update placeholders to generic examples (no association-specific data)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-20 12:49:24 +01:00
parent ffa6e04a2e
commit 8b3b1dab13
12 changed files with 911 additions and 0 deletions

View File

@@ -281,6 +281,47 @@ class AccountController extends AbstractController
return $this->redirectToRoute('app_account', ['tab' => 'subaccounts']);
}
#[Route('/mon-compte/evenement/creer', name: 'app_account_create_event', methods: ['GET', 'POST'])]
public function createEvent(Request $request, EntityManagerInterface $em): Response
{
$this->denyAccessUnlessGranted('ROLE_ORGANIZER');
/** @var User $user */
$user = $this->getUser();
if ($request->isMethod('POST')) {
$event = new \App\Entity\Event();
$event->setAccount($user);
$event->setTitle(trim($request->request->getString('title')));
$event->setDescription(trim($request->request->getString('description')) ?: null);
$event->setStartAt(new \DateTimeImmutable($request->request->getString('start_at')));
$event->setEndAt(new \DateTimeImmutable($request->request->getString('end_at')));
$event->setAddress(trim($request->request->getString('address')));
$event->setZipcode(trim($request->request->getString('zipcode')));
$event->setCity(trim($request->request->getString('city')));
$pictureFile = $request->files->get('event_main_picture');
if ($pictureFile) {
$event->setEventMainPictureFile($pictureFile);
}
$em->persist($event);
$em->flush();
$this->addFlash('success', 'Evenement cree avec succes.');
return $this->redirectToRoute('app_account', ['tab' => 'events']);
}
return $this->render('account/create_event.html.twig', [
'breadcrumbs' => [
self::BREADCRUMB_HOME,
self::BREADCRUMB_ACCOUNT,
['name' => 'Creer un evenement', 'url' => '/mon-compte/evenement/creer'],
],
]);
}
/** @codeCoverageIgnore Test helper, not used in production */
#[Route('/mon-compte/test-payout', name: 'app_account_test_payout', methods: ['POST'])]
public function testPayout(EntityManagerInterface $em): Response