Commit 38c65e5e by Yvon

first full working flow with two steps questionnaire (still not finished)

parent 004c3d7e
......@@ -8,15 +8,21 @@ use App\Entity\AccountPrestataire;
use App\Entity\AccountSiege;
use App\Entity\Comptoir;
use App\Entity\Geoloc;
use App\Entity\GeolocPrestataire;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use App\Enum\CurrencyEnum;
use App\Form\Type\DistributorSelfEvalPrestaQuizType;
use App\Form\Type\InfosPrestaQuizType;
use App\Form\Type\InstallFormType;
use App\Form\Type\ProducerSelfEvalPrestaQuizType;
use App\Security\LoginAuthenticator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\FOSUserEvents;
......@@ -35,11 +41,11 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Security as Secur;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Core\Security;
class IndexController extends AbstractController
{
......@@ -76,7 +82,7 @@ class IndexController extends AbstractController
$this->tokenStorage = $tokenStorage;
$this->router = $router;
$this->security = $security;
}
}
/**
* @Route("/", name="index")
......@@ -105,16 +111,20 @@ class IndexController extends AbstractController
}
if (
isset($_GET["upswd"])
&& $_GET["upswd"] == "success"
isset($_GET['upswd'])
&& 'success' == $_GET['upswd']
&& $this->security->isGranted('ROLE_ADHERENT')
&& $this->getParameter('tav_env') == true
&& true == $this->getParameter('tav_env')
) {
$this->addFlash(
'warning',
'Suite à la modification de votre mot de passe, pensez à mettre à jour votre code de paiment pour pouvoir effectuer des achats.'
);
}
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('index');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
return $this->render('@kohinos/' . $template, [
'news' => [],
......@@ -123,6 +133,63 @@ class IndexController extends AbstractController
]);
}
private function getAndDispatchPrestataire($origin)
{
if (!$this->getParameter('presta_self_init_and_eval')
|| !$this->security->isGranted('ROLE_ADHERENT')) { //not sure ROLE_ADHERENT is the proper check here
$destination = 'index';
return $destination !== $origin ? [$destination, null] : [null, null];
}
/* @var ArrayCollection $prestas */
if ($this->em->getFilters()->isEnabled('enabled_filter')) {
$this->em->getFilters()->disable('enabled_filter');
}
$prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $this->security->getUser()]);
if (!$this->em->getFilters()->isEnabled('enabled_filter')) {
$this->em->getFilters()->enable('enabled_filter');
}
if (!$prestas) {
$destination = 'index';
return $destination !== $origin ? [$destination, null] : [null, null];
}
/* @var Prestataire $presta */
$presta = $prestas[0];
if ($presta->isEnabled()) {
$destination = 'index';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
if ($presta->getSelfEvalPrestaQuiz()) {
$destination = 'prestaquiz-sent';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
if ($presta->getMarketChannelFunction()) {
$destination = 'prestaquiz-selfeval';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
$destination = 'prestaquiz-infos';
return $destination !== $origin ? [$destination, $presta] : [null, $presta];
}
/**
* @Route("/prestaquiz-sent", name="prestaquiz-sent")
*/
public function prestaQuizSent(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-sent');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
return new Response("Votre questionnaire a bien été reçu. Nous allons vous contacter. Vous n'avez rien à faire de plus pour l'instant.");
}
/**
* @Route("/manifest.json", name="manifest")
*/
......@@ -140,6 +207,80 @@ class IndexController extends AbstractController
}
/**
* @Route("/prestaquiz-infos", name="prestaquiz-infos")
*/
public function prestaQuizInfosAction(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
/* @var Prestataire $presta */
if(!$presta->getGeolocs() || $presta->getGeolocs()->isEmpty()) {
$address = new GeolocPrestataire();
$presta->addGeoloc($address);
}
$form = $this->createForm(InfosPrestaQuizType::class, $presta);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->security->getUser()->setFirstName($form->get('userFirstName')->getData());
$this->security->getUser()->setLastName($form->get('userLastName')->getData());
$this->em->flush();
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
}
return $this->render('@kohinos/tav/infos_prestaquiz.html.twig', [
'form' => $form->createView(),
]);
}
/**
* @Route("/prestaquiz-selfeval", name="prestaquiz-selfeval")
*/
public function prestaQuizSelfEvalAction(Request $request): Response
{
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
/* @var Prestataire $presta */
$formClass = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;
$quiz = new SelfEvalPrestaQuiz();
$form = $this->createForm($formClass, $quiz);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$presta->setSelfEvalPrestaQuiz($quiz);
$this->em->persist($quiz);
$this->em->flush();
list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
if ($redirRoute) {
return $this->redirectToRoute($redirRoute);
}
}
$tmpl = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
'@kohinos/tav/distributor_selfeval_prestaquiz.html.twig'
: '@kohinos/tav/producer_selfeval_prestaquiz.html.twig';
return $this->render($tmpl, [
'form' => $form->createView(),
]);
}
/**
* @Route("/installation", name="installation")
*/
public function installationAction(Request $request)
......@@ -379,7 +520,7 @@ class IndexController extends AbstractController
$this->session->set('_groupegere', $groupe);
$this->reloadUserTokenFromGroup($group, $request);
if ($this->getParameter('tav_env') == true) {
if (true == $this->getParameter('tav_env')) {
return $this->redirectToRoute('index');
} else {
return $this->redirectToRoute('sonata_admin_dashboard');
......@@ -412,7 +553,7 @@ class IndexController extends AbstractController
$this->session->set('_comptoirgere', $comptoir);
$this->reloadUserTokenFromGroup($group, $request);
if ($this->getParameter('tav_env') == true) {
if (true == $this->getParameter('tav_env')) {
return $this->redirectToRoute('index');
} else {
return $this->redirectToRoute('sonata_admin_dashboard');
......
......@@ -44,6 +44,9 @@ class Prestataire extends AccountableObject implements AccountableInterface
use HasEcompteEntity;
use HasAccountsTrait;
const DISTRIBUTOR = 'distributor';
const PRODUCER = 'producer';
/**
* @var \Ramsey\Uuid\UuidInterface
*
......@@ -198,6 +201,22 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $horaires;
/**
* @var string|null (champ libre)
*
* @ORM\Column(name="marketchannelfunction", type="text", nullable=true)
* @Groups({"read", "write"})
*/
private $marketchannelfunction;
public function setMarketChannelFunction($var)
{
$this->marketchannelfunction = $var;
}
public function getMarketChannelFunction()
{
return $this->marketchannelfunction;
}
/**
* @var TypePrestataire
*
* @ORM\ManyToOne(targetEntity="TypePrestataire", cascade={"persist"}, inversedBy="prestataires")
......@@ -206,6 +225,21 @@ class Prestataire extends AccountableObject implements AccountableInterface
private $typeprestataire;
/**
* @var SelfEvalPrestaQuiz
* @ORM\OneToOne(targetEntity="SelfEvalPrestaQuiz", cascade={"persist"})
* @ORM\JoinColumn(name="selfevalprestaquiz_id", referencedColumnName="id", nullable=true)
*/
protected $selfevalprestaquiz;
public function setSelfEvalPrestaQuiz($var)
{
$this->selfevalprestaquiz = $var;
}
public function getSelfEvalPrestaQuiz()
{
return $this->selfevalprestaquiz;
}
/**
* @var ArrayCollection|Rubrique[]
* @ORM\ManyToMany(targetEntity="Rubrique", mappedBy="prestataires", cascade={"persist"}, fetch="EXTRA_LAZY")
* @Groups({"read", "write"})
......@@ -303,7 +337,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
/**
* Caissiers can export all the transactions since the last export.
*
*
* @ORM\Column(type="datetime", nullable=true)
*/
private $lastTransactionsExportDatetime;
......@@ -712,7 +746,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
return $return;
}, $this->users->getValues()));
}
}
public function getGestionnairesEmailsString()
{
......@@ -748,7 +782,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
*
* @return $this
*/
public function removeUser(User $users)
public function removeUser(User $user)
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
......@@ -790,7 +824,7 @@ class Prestataire extends AccountableObject implements AccountableInterface
*
* @return $this
*/
public function removeCaissier(User $caissiers)
public function removeCaissier(User $caissier)
{
if ($this->caissiers->contains($caissier)) {
$this->caissiers->removeElement($caissier);
......
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity
* @ORM\Table(name="selfevalprestaquiz")
*/
class SelfEvalPrestaQuiz
{
/*
* This class is really just about storing results of a form in a database.
* Attributes are not going to be part of any logic code.
* I opt to set all attributes public to save coding time.
*/
/**
* @var \Ramsey\Uuid\UuidInterface
*
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
* @Groups({"read"})
*/
protected $id;
/**
* @var int|null
* @ORM\Column(name="accessib_geophy", type="integer")
*/
public $accessib_geophy;
/**
* @var int|null
* @ORM\Column(name="accessib_tempor", type="integer")
*/
public $accessib_tempor;
/**
* @var int|null
* @ORM\Column(name="accessib_edupop", type="integer")
*/
public $accessib_edupop;
/**
* @var int|null
* @ORM\Column(name="accessib_divers", type="integer")
*/
public $accessib_divers;
/**
* @var int|null
* @ORM\Column(name="accessib_vulner", type="integer")
*/
public $accessib_vulner;
/**
* @var string|null
* @ORM\Column(name="accessib_geophy_comment", type="text")
*/
public $accessib_geophy_comment;
/**
* @var string|null
* @ORM\Column(name="accessib_tempor_comment", type="text")
*/
public $accessib_tempor_comment;
/**
* @var string|null
* @ORM\Column(name="accessib_edupop_comment", type="text")
*/
public $accessib_edupop_comment;
/**
* @var string|null
* @ORM\Column(name="accessib_divers_comment", type="text")
*/
public $accessib_divers_comment;
/**
* @var string|null
* @ORM\Column(name="accessib_vulner_comment", type="text")
*/
public $accessib_vulner_comment;
}
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DistributorSelfEvalPrestaQuizType extends SelfEvalPrestaQuizType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
}
/**
*
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SelfEvalPrestaQuiz::class,
]);
}
public function getBlockPrefix(): string
{
return 'formDistributorSelfEvalPrestaQuiz';
}
}
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotNull;
class InfosPrestaQuizType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('userFirstName', TextType::class, [
'mapped' => false, //store it into the user
'label' => 'Prénom',
'required' => true,
'constraints' => [new NotNull()],
])
->add('userLastName', TextType::class, [
'mapped' => false, //store it into the user
'label' => 'Nom',
'required' => true,
'constraints' => [new NotNull()],
])
->add('raison', TextType::class, [
'required' => true,
'constraints' => [new NotNull()],
])
->add('responsable', TextType::class, [
'required' => true,
'constraints' => [new NotNull()],
])
->add('metier', TextType::class, [
'label' => 'Métier responsable',
'required' => true,
'constraints' => [new NotNull()],
])
->add('horaires', TextType::class, [
'label' => 'Jours, horaires et périodes de fermeture pour congés :',
'required' => true,
'constraints' => [new NotNull()],
])
->add('geolocs', CollectionType::class, [
'entry_type' => GeolocPrestataireFormType::class,
'entry_options' => ['label' => false, 'with_latlon' => false],
'required' => true,
'allow_add' => false,
'allow_delete' => false,
'by_reference' => false,
'label' => false,
])
->add('marketchannelfunction', ChoiceType::class, [
'label' => 'Fonction dans le circuit de distribution',
'choices' => [
'Distributeur/distributrice' => Prestataire::DISTRIBUTOR,
'Producteur/productrice' => Prestataire::PRODUCER,
],
'required' => true,
'constraints' => [new NotNull()],
])
->add('save', SubmitType::class, ['label' => 'Envoyer'])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Prestataire::class,
]);
}
public function getBlockPrefix(): string
{
return 'formInfosPrestaQuiz';
}
}
<?php
namespace App\Form\Type;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProducerSelfEvalPrestaQuizType extends SelfEvalPrestaQuizType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
}
/**
*
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SelfEvalPrestaQuiz::class,
]);
}
public function getBlockPrefix(): string
{
return 'formProducerSelfEvalPrestaQuiz';
}
}
<?php
namespace App\Form\Type;
use App\Entity\SelfEvalPrestaQuiz;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotNull;
class SelfEvalPrestaQuizType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$faceStyling = function($choice, $key, $value) {
$icons = [
'0' => 'fas fa-frown text-danger',
'1' => 'fas fa-meh text-warning',
"2" => 'fas fa-smile text-success'
];
return ['class' => $icons[$value] ?? '',];
};
/*
private $bienetre_tran
private $transpar
private $agridura
private $locaprod
*/
$builder
->add('accessib_geophy', ChoiceType::class, [
'label' => 'Géographique et physique : le lieu est-il accessible par différents mode de transport ?',
'choices' => [
'0 mode de transport' => 0,
'1 mode de transport' => 1,
"plus d'1 mode de transport" => 2,
],
'choice_attr' => $faceStyling,
'expanded' => true,
'constraints' => [new NotNull()]
])
->add('accessib_geophy_comment', TextareaType::class, [
'attr'=> ['placeholder' => 'Commentaires'],
'label' => false,
'required' => false,
])
->add('accessib_tempor', ChoiceType::class, [
'label' => "Temporelle : le lieu communique-t-il sur ses plages d'ouvertures ?",
'choices' => [
'0 mode de communication' => 0,
'1 mode de communication' => 1,
"plus d'1 mode de communication" => 2,
],
'choice_attr' => $faceStyling,
'expanded' => true,
'constraints' => [new NotNull()]
])
->add('accessib_tempor_comment', TextareaType::class, [
'attr'=> ['placeholder' => 'Commentaires'],
'label' => false,
'required' => false
])
->add('accessib_edupop', ChoiceType::class, [
'label' => "Sociale et inclusivité (1/3) : le lieu mène-t-il des actions de sensibilisation et d'éducation populaire à l'alimentation ?",
'choices' => [
'non' => 0,
'moyennement' => 1,
'oui' => 2,
],
'choice_attr' => $faceStyling,
'expanded' => true,
'constraints' => [new NotNull()]
])
->add('accessib_edupop_comment', TextareaType::class, [
'attr'=> ['placeholder' => 'Commentaires'],
'label' => false,
'required' => false
])
->add('accessib_divers', ChoiceType::class, [
'label' => 'Sociale et inclusivité (2/3) : le lieu propose-t-il des services spécifiques pour faciliter son accessibilité à une diversité
de profils (pour les personnes agées, les personnes en situation de handicatp, les familles ...) ?',
'choices' => [
'non' => 0,
'moyennement' => 1,
'oui' => 2,
],
'choice_attr' => $faceStyling,
'expanded' => true,
'constraints' => [new NotNull()]
])
->add('accessib_divers_comment', TextareaType::class, [
'attr'=> ['placeholder' => 'Commentaires'],
'label' => false,
'required' => false
])
->add('accessib_vulner', ChoiceType::class, [
'label' => 'Sociale et inclusivité (3/3) : le lieu mène-t-il des actions spécifiques pour lutter contre la vulnérabilité alimentaire ?',
'choices' => [
'non' => 0,
'moyennement' => 1,
'oui' => 2,
],
'choice_attr' => $faceStyling,
'expanded' => true,
'constraints' => [new NotNull()]
])
->add('accessib_vulner_comment', TextareaType::class, [
'attr'=> ['placeholder' => 'Commentaires'],
'label' => false,
'required' => false
])
->add('save', SubmitType::class, ['label' => 'Envoyer'])
;
}
/**
*
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SelfEvalPrestaQuiz::class,
]);
}
public function getBlockPrefix(): string
{
return 'formSelfEvalPrestaQuiz';
}
}
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
{% form_theme form '@kohinos/tav/prestaquiz_form_theme.html.twig' %}
{% include '@kohinos/tav/selfeval_prestaquiz.html.twig' %}
{% endblock %}
\ No newline at end of file
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
<div class='container' style='max-width: 800px;'>
<h1>Inscription point de vente (1/2) : identification</h1>
{{ form_start(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
\ No newline at end of file
{% block choice_widget %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% if expanded %}
{%- for child in form %}
<div class="form-check">
<input type="checkbox" id="{{ child.vars.id }}" name="{{ child.vars.full_name }}" value="{{ child.vars.value }}" class="form-check-input">
<label class="form-check-label" for="{{ child.vars.id }}">
<i class="{{ child.vars.attr['class'] }}"></i> {{ child.vars.label }}
</label>
</div>
{%- endfor -%}
{% else %}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if placeholder is not none %}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder|trans({}, translation_domain) }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{% set options = preferred_choices %}
{{ block('choice_widget_options') }}
{% if choices|length > 0 and separator is not none %}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('choice_widget_options') }}
</select>
{% endif %}
</div>
{% endspaceless %}
{% endblock choice_widget %}
\ No newline at end of file
{% extends '@kohinos/common/layout.html.twig' %}
{% block content %}
{% form_theme form '@kohinos/tav/prestaquiz_form_theme.html.twig' %}
{% include '@kohinos/tav/selfeval_prestaquiz.html.twig' %}
{% endblock %}
\ No newline at end of file
<h1>Inscription point de vente (2/2) : auto-évaluation</h1>
{{ form_start(form) }}
<h2>Accessibilité et Inclusivité</h2>
<div class="container">
<div class="row">
{{ form_label(form.accessib_geophy) }}
<div class="col">{{ form_widget(form.accessib_geophy) }}</div>
<div class="col">{{ form_row(form.accessib_geophy_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_tempor) }}
<div class="col">{{ form_widget(form.accessib_tempor) }}</div>
<div class="col">{{ form_row(form.accessib_tempor_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_edupop) }}
<div class="col">{{ form_widget(form.accessib_edupop) }}</div>
<div class="col">{{ form_row(form.accessib_edupop_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_divers) }}
<div class="col">{{ form_widget(form.accessib_divers) }}</div>
<div class="col">{{ form_row(form.accessib_divers_comment) }}</div>
</div>
</div>
<div class="container">
<div class="row">
{{ form_label(form.accessib_vulner) }}
<div class="col">{{ form_widget(form.accessib_vulner) }}</div>
<div class="col">{{ form_row(form.accessib_vulner_comment) }}</div>
</div>
</div>
{{ form_end(form) }}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment