Skip to content

Commit

Permalink
QR Code Sponsor : nouvelle page et ajout d'un scan par form (#1492)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerlio committed Jul 4, 2024
1 parent ca075d8 commit 03e01c5
Show file tree
Hide file tree
Showing 14 changed files with 512 additions and 1 deletion.
11 changes: 10 additions & 1 deletion app/Resources/translations/messages.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,13 @@ speaker_from_previous_event: "We filled the form with the profile you used on a
"Personne référente": "Liaison"
"sera votre personne de contact privilégiée dans les semaines menant à l’évènement tout comme durant l’évènement. N’hésitez pas à prendre contact pour toutes questions relatives à votre venue.": "will be your main contact person in the weeks leading up to the event as well as during the event. Please do not hesitate to contact us if you have any question about your visit."
"Plusieurs évènements en ce moment !": "Several events at the moment!"
"Évènements": "Events"
"Évènements": "Events"
"Espace sponsor": "Sponsor space"
"Billetterie sponsors": "Sponsor tickets"
"QR Codes": "QR Codes"
"SCANNEUR DE QR CODES": "QR Codes Scanner"
"Scanner un QR Code": "Scan a QR Code"
"QR Codes scannés": "Scanned QR Codes"
"Télécharger en CSV": "Download CSV"
"Supprimer": "Delete"
"Lancer l'outil de scan de QR Codes": "Run QR Code Scanner"
7 changes: 7 additions & 0 deletions app/Resources/views/event/sponsor/base.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends ':event:base.html.twig' %}

{% block title %}{{ 'Espace sponsor'|trans }} - Afup{% endblock %}

{% if menu is not defined %}
{% set menu = {'main': {'route' : 'sponsor_ticket_form', 'title': 'Billetterie sponsors'}} %}
{% endif %}
29 changes: 29 additions & 0 deletions app/Resources/views/event/sponsor/scan.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% extends ':event/sponsor:base.html.twig' %}

{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('/css/sponsor_scan.css') }}">
{% endblock %}

{% block content %}
<div class="row">
<div class="col-md-6">
<h1>{{ 'QR Codes'|trans }} : {{ event.title }} - {{ sponsorTicket.company }}</h1>
<h2 class="h3-like">{% trans %}SCANNEUR DE QR CODES{% endtrans %}</h2>
<a class="button sponsor_scan" href="{{ url('sponsor_scan_new', {eventSlug: event.path}) }}">{% trans %}Scanner un QR Code{% endtrans %}</a>

<h2 class="h3-like">{% trans %}QR CODES SCANNÉS{% endtrans %}</h2>
<a class="button button__medium" href="#">{% trans %}Télécharger en CSV{% endtrans %}</a>

<dl>
{% for scan in scans %}
<dt><strong>{{ scan.prenom }} {{ scan.nom }}</strong></dt>
<dd class="mbs">
{{ scan.email }}<br />
{{ scan.created_on|date('d/m/Y H:i')}} - <a href="#">{% trans %}Supprimer{% endtrans %}</a>
</dd>
{% endfor %}
</dl>
</div>
</div>
{% endblock %}
9 changes: 9 additions & 0 deletions app/Resources/views/event/sponsor/scan_new.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% extends ':event/sponsor:base.html.twig' %}

{% block content %}
<div class="row">
<div class="col-md-6">
{{ form(form) }}
</div>
</div>
{% endblock %}
7 changes: 7 additions & 0 deletions app/Resources/views/event/ticket/sponsor.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
<div class="col-md-6">
<h1>{{ 'Billetterie sponsor'|trans }}: {{ event.title }} - {{ sponsorTicket.company }}</h1>

{% if sponsorTicket.qrCodesScannerAvailable %}
<h2 class="h3-like">{% trans %}SCANNEUR DE QR CODES{% endtrans %}</h2>
<a class="button button__medium" href="{{ url('sponsor_scan', {eventSlug: event.path}) }}">
{% trans %}Lancer l'outil de scan de QR Codes{% endtrans %}
</a>
{% endif %}

<h2 class="h3-like">{% trans %}VOS PLACES GRATUITES{% endtrans %}</h2>

{% if sold_out %}
Expand Down
12 changes: 12 additions & 0 deletions app/config/routing/event.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ sponsor_leads_post:
path: /{eventSlug}/sponsor/thank-you
defaults: {_controller: AppBundle:Lead:postLead}

sponsor_scan:
path: /{eventSlug}/sponsor/scan
defaults: {_controller: AppBundle:SponsorScan:index}

sponsor_scan_new:
path: /{eventSlug}/sponsor/scan/new
defaults: {_controller: AppBundle:SponsorScan:new}

sponsor_scan_flash:
path: /{eventSlug}/sponsor/scan/flash/{code}
defaults: {_controller: AppBundle:SponsorScan:flash}

speaker-infos:
path: /{eventSlug}/speaker-infos
defaults: {_controller: AppBundle\Controller\Event\SpeakerPageAction }
Expand Down
28 changes: 28 additions & 0 deletions db/migrations/20240422143329_qr_code.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php


use Phinx\Migration\AbstractMigration;

class QrCode extends AbstractMigration
{
public function change()
{
$sql = <<<EOF
ALTER TABLE `afup_inscription_forum`
ADD `qr_code` VARCHAR(10);
EOF;
$this->execute($sql);

$sql = <<<EOF
CREATE TABLE IF NOT EXISTS `afup_forum_sponsor_scan` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`sponsor_ticket_id` INT(11) NOT NULL,
`ticket_id` INT(11) NOT NULL,
`created_on` DATETIME NOT NULL,
`deleted_on` DATETIME,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
EOF;
$this->execute($sql);
}
}
8 changes: 8 additions & 0 deletions htdocs/css/sponsor_scan.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* en mobile */
@media (max-width: 460px) {
.button.sponsor_scan {
display: block;
margin: 0 auto;
width: 80%;
}
}
134 changes: 134 additions & 0 deletions sources/AppBundle/Controller/SponsorScanController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller;

use AppBundle\Event\Form\SponsorScanType;
use AppBundle\Event\Model\Repository\SponsorScanRepository;
use AppBundle\Event\Model\Repository\SponsorTicketRepository;
use AppBundle\Event\Model\Repository\TicketRepository;
use AppBundle\Event\Model\SponsorScan;
use AppBundle\Event\Model\SponsorTicket;
use AppBundle\Event\Model\Ticket;
use Symfony\Component\HttpFoundation\Request;

class SponsorScanController extends EventBaseController
{
public function indexAction(Request $request, $eventSlug)
{
$event = $this->checkEventSlug($eventSlug);

try {
$sponsorTicket = $this->checkSponsorTicket($request);
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('sponsor_ticket_home', ['eventSlug' => $eventSlug]);
}

/** @var SponsorScanRepository $scanRepository */
$scanRepository = $this->get('ting')->get(SponsorScanRepository::class);
$scans = $scanRepository->getBySponsorTicket($sponsorTicket);

return $this->render(':event/sponsor:scan.html.twig', [
'event' => $event,
'sponsorTicket' => $sponsorTicket,
'scans' => $scans,
]);
}

public function newAction(Request $request, $eventSlug)
{
$event = $this->checkEventSlug($eventSlug);

try {
$this->checkSponsorTicket($request);
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('sponsor_ticket_home', ['eventSlug' => $eventSlug]);
}

$form = $this->createForm(SponsorScanType::class);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();

return $this->redirectToRoute('sponsor_scan_flash', [
'eventSlug' => $event->getPath(),
'code' => $data['code'],
]);
}

return $this->render(':event/sponsor:scan_new.html.twig', [
'event' => $event,
'form' => $form->createView(),
]);
}

public function flashAction(Request $request, string $code, string $eventSlug)
{
$event = $this->checkEventSlug($eventSlug);

try {
$sponsorTicket = $this->checkSponsorTicket($request);
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('sponsor_ticket_home', ['eventSlug' => $eventSlug]);
}

/** @var TicketRepository $ticketRepository */
$ticketRepository = $this->get('ting')->get(TicketRepository::class);
/** @var Ticket $ticket */
$ticket = $ticketRepository->getOneBy(['forumId' => $event->getId(), 'qrCode' => $code]);

if ($ticket === null) {
$this->addFlash('error', 'Code inexistant ou invalide');
return $this->redirectToRoute('sponsor_scan', ['eventSlug' => $eventSlug]);
}

/** @var SponsorScanRepository $scanRepository */
$scanRepository = $this->get('ting')->get(SponsorScanRepository::class);
$scan = $scanRepository->getOneBy(['sponsorTicketId' => $sponsorTicket->getId(), 'ticketId' => $ticket->getId()]);

if ($scan instanceof SponsorScan && $scan->getDeletedOn() === null) {
$this->addFlash('error', 'Code déjà scanné.');
return $this->redirectToRoute('sponsor_scan', ['eventSlug' => $eventSlug]);
}

if (!$scan instanceof SponsorScan) {
$scan = (new SponsorScan())
->setSponsorTicketId($sponsorTicket->getId())
->setTicketId($ticket->getId());
}

$scan->setCreatedOn(new \DateTime('now'))
->setDeletedOn(null);
$scanRepository->save($scan);

$this->addFlash('success', 'QR Code ajouté !');

return $this->redirectToRoute('sponsor_scan', ['eventSlug' => $eventSlug]);
}

private function checkSponsorTicket(Request $request)
{
if ($request->getSession()->has('sponsor_ticket_id') === false) {
throw new \Exception('Merci de renseigner votre token.');
}

/**
* @var SponsorTicket $sponsorTicket
*/
$sponsorTicket = $this->get('ting')->get(SponsorTicketRepository::class)->get($request->getSession()->get('sponsor_ticket_id'));
if ($sponsorTicket === null) {
throw new \Exception('Token invalide.');
}

if (!$sponsorTicket->getQrCodesScannerAvailable()) {
throw new \Exception('Accès non autorisé.');
}

return $sponsorTicket;
}
}
28 changes: 28 additions & 0 deletions sources/AppBundle/Event/Form/SponsorScanType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace AppBundle\Event\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

class SponsorScanType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', TextType::class, [
'required' => true,
'constraints' => [
new NotBlank(),
],
'label' => 'Code',
])
->add('save', SubmitType::class, ['label' => 'Valider'])
;
}
}
77 changes: 77 additions & 0 deletions sources/AppBundle/Event/Model/Repository/SponsorScanRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace AppBundle\Event\Model\Repository;

use AppBundle\Event\Model\SponsorScan;
use AppBundle\Event\Model\SponsorTicket;
use CCMBenchmark\Ting\Repository\HydratorArray;
use CCMBenchmark\Ting\Repository\Metadata;
use CCMBenchmark\Ting\Repository\MetadataInitializer;
use CCMBenchmark\Ting\Repository\Repository;
use CCMBenchmark\Ting\Serializer\SerializerFactoryInterface;

class SponsorScanRepository extends Repository implements MetadataInitializer
{
public function getBySponsorTicket(SponsorTicket $sponsorTicket)
{
$sql = 'SELECT afup_forum_sponsor_scan.id,
afup_inscription_forum.nom,
afup_inscription_forum.prenom,
afup_inscription_forum.email,
afup_forum_sponsor_scan.created_on
FROM afup_forum_sponsor_scan
INNER JOIN afup_inscription_forum ON afup_inscription_forum.id = afup_forum_sponsor_scan.ticket_id
AND afup_forum_sponsor_scan.sponsor_ticket_id = :sponsorTicketId
AND afup_forum_sponsor_scan.deleted_on IS NULL
ORDER BY afup_forum_sponsor_scan.created_on DESC';

return $this->getPreparedQuery($sql)
->setParams(['sponsorTicketId' => $sponsorTicket->getId()])
->query($this->getCollection(new HydratorArray()));
}

/**
* @inheritDoc
*/
public static function initMetadata(SerializerFactoryInterface $serializerFactory, array $options = [])
{
$metadata = new Metadata($serializerFactory);

$metadata->setEntity(SponsorScan::class);
$metadata->setConnectionName('main');
$metadata->setDatabase($options['database']);
$metadata->setTable('afup_forum_sponsor_scan');

$metadata
->addField([
'columnName' => 'id',
'fieldName' => 'id',
'primary' => true,
'autoincrement' => true,
'type' => 'int'
])
->addField([
'columnName' => 'sponsor_ticket_id',
'fieldName' => 'sponsorTicketId',
'type' => 'string'
])
->addField([
'columnName' => 'ticket_id',
'fieldName' => 'ticketId',
'type' => 'int'
])
->addField([
'columnName' => 'created_on',
'fieldName' => 'createdOn',
'type' => 'datetime'
])
->addField([
'columnName' => 'deleted_on',
'fieldName' => 'deletedOn',
'type' => 'datetime'
])
;

return $metadata;
}
}
5 changes: 5 additions & 0 deletions sources/AppBundle/Event/Model/Repository/TicketRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ public static function initMetadata(SerializerFactoryInterface $serializerFactor
'fieldName' => 'transportDistance',
'type' => 'int'
])
->addField([
'columnName' => 'qr_code',
'fieldName' => 'qrCode',
'type' => 'string',
])
;

return $metadata;
Expand Down
Loading

0 comments on commit 03e01c5

Please sign in to comment.