diff --git a/migrations/Version20260829120000.php b/migrations/Version20260829120000.php new file mode 100644 index 0000000..116e9b2 --- /dev/null +++ b/migrations/Version20260829120000.php @@ -0,0 +1,26 @@ +addSql('CREATE TABLE contact_message (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, message LONGTEXT NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP TABLE contact_message'); + } +} diff --git a/src/Game/Controller/GameAdminContactController.php b/src/Game/Controller/GameAdminContactController.php new file mode 100644 index 0000000..69b7252 --- /dev/null +++ b/src/Game/Controller/GameAdminContactController.php @@ -0,0 +1,24 @@ +render('game/admin/contact/index.html.twig', [ + 'messages' => $contactMessageRepository->findBy([], ['createdAt' => 'DESC']), + ]); + } +} diff --git a/src/Website/Controller/ContactController.php b/src/Website/Controller/ContactController.php new file mode 100644 index 0000000..5ffafd8 --- /dev/null +++ b/src/Website/Controller/ContactController.php @@ -0,0 +1,34 @@ +createForm(ContactFormType::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->persist($form->getData()); + $entityManager->flush(); + + $this->addFlash('success', 'Thanks for reaching out — your message has been sent.'); + + return $this->redirectToRoute('website_contact'); + } + + return $this->render('website/contact.html.twig', [ + 'contactForm' => $form->createView(), + ]); + } +} diff --git a/src/Website/Entity/ContactMessage.php b/src/Website/Entity/ContactMessage.php new file mode 100644 index 0000000..1aca114 --- /dev/null +++ b/src/Website/Entity/ContactMessage.php @@ -0,0 +1,87 @@ +createdAt = new \DateTimeImmutable(); + } + + 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 getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): static + { + $this->email = $email; + + return $this; + } + + public function getMessage(): ?string + { + return $this->message; + } + + public function setMessage(string $message): static + { + $this->message = $message; + + return $this; + } + + public function getCreatedAt(): ?\DateTimeImmutable + { + return $this->createdAt; + } + + public function setCreatedAt(\DateTimeImmutable $createdAt): static + { + $this->createdAt = $createdAt; + + return $this; + } +} diff --git a/src/Website/Form/ContactFormType.php b/src/Website/Form/ContactFormType.php new file mode 100644 index 0000000..350e1a3 --- /dev/null +++ b/src/Website/Form/ContactFormType.php @@ -0,0 +1,53 @@ +add('name', TextType::class, [ + 'constraints' => [ + new NotBlank(message: 'Please enter your name'), + new Length(max: 255, maxMessage: 'Your name cannot be longer than {{ limit }} characters'), + ], + ]) + ->add('email', EmailType::class, [ + 'constraints' => [ + new NotBlank(message: 'Please enter your email address'), + ], + ]) + ->add('message', TextareaType::class, [ + 'attr' => ['rows' => 6], + 'constraints' => [ + new NotBlank(message: 'Please enter a message'), + new Length(max: 5000, maxMessage: 'Your message cannot be longer than {{ limit }} characters'), + ], + ]) + ->add('captcha', Recaptcha3Type::class, [ + 'constraints' => new Recaptcha3(), + 'action_name' => 'contact', + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => ContactMessage::class, + ]); + } +} diff --git a/src/Website/Repository/ContactMessageRepository.php b/src/Website/Repository/ContactMessageRepository.php new file mode 100644 index 0000000..2634367 --- /dev/null +++ b/src/Website/Repository/ContactMessageRepository.php @@ -0,0 +1,18 @@ + + */ +class ContactMessageRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ContactMessage::class); + } +} diff --git a/templates/game/admin/base.html.twig b/templates/game/admin/base.html.twig index fa89963..08d1266 100644 --- a/templates/game/admin/base.html.twig +++ b/templates/game/admin/base.html.twig @@ -136,6 +136,7 @@ { route: 'game_admin_games', label: 'Games', icon: '🎮' }, { route: 'game_admin_sessions', label: 'Sessions', icon: '▶' }, { route: 'game_admin_feedback', label: 'Feedback', icon: '💬' }, + { route: 'game_admin_contact', label: 'Contact', icon: '📨' }, { route: 'game_admin_email_log', label: 'Email Log', icon: '✉' }, ] %} diff --git a/templates/game/admin/contact/index.html.twig b/templates/game/admin/contact/index.html.twig new file mode 100644 index 0000000..504dd69 --- /dev/null +++ b/templates/game/admin/contact/index.html.twig @@ -0,0 +1,39 @@ +{% extends 'game/admin/base.html.twig' %} + +{% block title %}Contact Messages — Admin{% endblock %} + +{% block admin_body %} +