<?php
namespace App\Form;
use App\Entity\{Devis, DevisTravelReason, DevisLuggage, DevisEquipment, DevisBudget};
use Symfony\Component\Form\AbstractType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormError;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class DevisType extends AbstractType
{
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$previousTypeOfTrip = $this->session->get('typeOfTrip', 'Aller simple');
$previousNeedVehicleOnSite = $this->session->get('needVehicleOnSite', 'Non');
$previousAddPdfDocument = $this->session->get('addPdfDocument', 'Non');
if ($options['step'] === 'step1') {
$builder
->add('typeOfTrip', ChoiceType::class, [
'label' => false,
'choices' => [
'Aller simple' => 'Aller simple',
'Aller-Retour' => 'Aller-Retour',
'Séjours' => 'Séjours'
],
'expanded' => true,
'multiple' => false,
'required' => true,
'data' => $previousTypeOfTrip ?: 'Aller simple',
'constraints' => [
new NotBlank([
'message' => 'Veuillez sélectionner un type de trajet !',
]),
]
])
->add('departureAddress', TextType::class, [
'label' => '<i class="fal fa-map-marker-alt"></i> Lieu de départ',
'label_html' => true,
'constraints' => [
new NotBlank([
'message' => 'Veuillez entrer votre lieu de départ !',
]),
new Length([
'min' => 3,
'minMessage' => 'Lieu départ doit avoir au moins {{ limit }} caractères.',
]),
],
'attr' => [
'placeholder' => 'Choisissez votre adresse de départ',
'autocomplete' => 'off',
'class' => 'autocomplete-input'
],
'required' => true
])
->add('arrivalAddress', TextType::class, [
'label' => '<i class="fal fa-map-marker-alt"></i> Lieu d\'arrivée',
'label_html' => true,
'constraints' => [
new NotBlank([
'message' => 'Veuillez entrer votre lieu d\'arrivée !',
]),
new Length([
'min' => 3,
'minMessage' => 'Lieu d\'arrivée doit avoir au moins {{ limit }} caractères.',
]),
],
'attr' => [
'placeholder' => 'Choisissez votre adresse d\'arrivée',
'autocomplete' => 'off',
'class' => 'autocomplete-input'
],
'required' => true
])
->add('departureDate', DateType::class, [
'label' => '<i class="fal fa-calendar-check"></i> Date de départ',
'label_html' => true,
'widget' => 'single_text',
'html5' => false,
'format' => 'dd-MM-yyyy',
'attr' => [
'class' => 'flatpickr-devis-depart',
'placeholder' => 'jj-mm-aaaa',
'readonly' => true
],
'required' => true,
'constraints' => [
new NotBlank([
'message' => 'Veuillez entrer votre date de départ !',
])
]
])
->add('returnDate', DateType::class, [
'label' => '<i class="fal fa-calendar-check"></i> Date de retour',
'label_html' => true,
'widget' => 'single_text',
'html5' => false,
'format' => 'dd-MM-yyyy',
'attr' => [
'class' => 'flatpickr-devis-retour',
'placeholder' => 'jj-mm-aaaa',
'readonly' => true
],
'required' => false,
'constraints' => [
new Callback(function ($value, ExecutionContextInterface $context) {
$form = $context->getRoot();
$typeOfTrip = $form->get('typeOfTrip')->getData();
$departureDate = $form->get('departureDate')->getData();
if (in_array($typeOfTrip, ['Séjours']) && empty($value)) {
$context->buildViolation('Veuillez entrer votre date de retour !')
->atPath('returnDate')
->addViolation();
}
if (!empty($value) && $departureDate && $value < $departureDate) {
$context->buildViolation('La date de retour doit être postérieure à la date de départ.')
->atPath('returnDate')
->addViolation();
}
}),
],
])
->add('departureTime', TimeType::class, [
'label' => '<i class="fal fa-clock"></i> Heure de départ',
'label_html' => true,
'input' => 'datetime',
'widget' => 'single_text',
'constraints' => [
new NotBlank([
'message' => 'Veuillez entrer votre heure de départ !',
]),
],
'html5' => false,
'attr' => [
'class' => 'flatpickr-devis-depart-time',
'placeholder' => 'hh:mm',
'readonly' => true
],
'required' => true
])
->add('returnTime', TimeType::class, [
'label' => '<i class="fal fa-clock"></i> Heure de retour',
'label_html' => true,
'input' => 'datetime',
'widget' => 'single_text',
'html5' => false,
'attr' => [
'class' => 'flatpickr-devis-retour-time',
'placeholder' => 'hh:mm',
'readonly' => true
],
'required' => false,
'constraints' => [
new Callback(function ($value, ExecutionContextInterface $context) {
$form = $context->getRoot();
$typeOfTrip = $form->get('typeOfTrip')->getData();
$departureTime = $form->get('departureTime')->getData();
$departureDate = $form->get('departureDate')->getData();
$returnDate = $form->get('returnDate')->getData();
if (in_array($typeOfTrip, ['Aller-Retour', 'Séjours']) && empty($value)) {
$context->buildViolation('Veuillez entrer votre heure de retour !')
->atPath('returnTime')
->addViolation();
}
if (!empty($value)) {
if ($returnDate == $departureDate || empty($returnDate)) {
if ($value <= $departureTime) {
$context->buildViolation('L\'heure de retour doit être après l\'heure de départ.')
->atPath('returnTime')
->addViolation();
}
}
}
}),
],
])
->add('numberOfPeople', NumberType::class, [
'label' => 'Total passagers, enfants compris',
'attr' => [
'pattern' => '[0-9]+', // + pour indiquer un ou plusieurs chiffres
'min' => 1,
'step' => 1
],
'data' => 1,
'constraints' => [
new NotBlank([
'message' => 'Veuillez indiquer le nombre de passagers !',
])
],
'required' => true,
'html5' => true,
'data' => $options['data']->getNumberOfPeople() ?? 1
])
;
}
if ($options['step'] === 'step2') {
$builder
->add('needVehicleOnSite', ChoiceType::class, [
'label' => 'Une fois sur place avez vous besoin du véhicule?',
'choices' => [
'Oui' => 'Oui',
'Non' => 'Non'
],
'expanded' => true,
'multiple' => false,
// 'required' => false,
'data' => $previousNeedVehicleOnSite ?: 'Non',
'constraints' => [
new Callback(function ($value, ExecutionContextInterface $context) use ($options) {
$form = $context->getRoot();
$typeOfTrip = $form->getData()->getTypeOfTrip();
$vehicleDetails = $form->getData()->getVehicleDetails();
if (in_array($typeOfTrip, ['Aller-Retour', 'Séjours'])) {
if ($value === 'Oui' && count($vehicleDetails) === 0) {
$context->buildViolation('Veuillez indiquer si vous avez besoin d\'un véhicule sur place !')
->atPath('vehicleDetails')
->addViolation();
}
if (empty($value)) {
$context->buildViolation('Veuillez sélectionner OUI ou NON avant de continuer !')
->atPath('needVehicleOnSite')
->addViolation();
}
}
}),
],
])
->add('vehicleDetails', CollectionType::class, [
'entry_type' => DevisVehicleDetatilsType::class,
'entry_options' => [
'label' => false,
'add_delete' => true
],
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
'required' => false
])
->add('addPdfDocument', ChoiceType::class, [
'label' => 'Voulez vous ajouter un document en PDF?',
'choices' => [
'Oui' => 'Oui',
'Non' => 'Non'
],
'expanded' => true,
'multiple' => false,
'data' => $previousAddPdfDocument ?: 'Non',
'constraints' => [
new Callback(function ($value, ExecutionContextInterface $context) use ($options) {
$form = $context->getRoot();
$typeOfTrip = $form->getData()->getTypeOfTrip();
$attachments = $form->getData()->getAttachments();
if (in_array($typeOfTrip, ['Aller-Retour', 'Séjours'])) {
if ($value === 'Oui' && count($attachments) === 0) {
$context->buildViolation('Vous devez ajouter au moins l\'un de ces documents : PDF, JPEG, JPG, PNG.')
->atPath('attachments')
->addViolation();
}
if (empty($value)) {
$context->buildViolation('Veuillez sélectionner OUI ou NON avant de continuer !')
->atPath('addPdfDocument')
->addViolation();
}
}
})
]
])
->add('attachments', CollectionType::class, [
'entry_type' => DevisAttachmentType::class,
'entry_options' => [
'label' => false,
'add_delete' => true
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false
])
->add('devisTravelReason', EntityType::class, [
'label' => 'Motif de déplacement',
'attr' => [
'class' => 'chosen-select no-search-select pl-3 mb-0',
],
'placeholder' => 'Sélectionner',
'class' => devisTravelReason::class,
'choice_label' => function (devisTravelReason $motif) {
return $motif->getName();
},
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('dt')
->orderBy('dt.id', 'DESC');
},
'constraints' => [
new NotBlank([
'message' => 'Veuillez sélectionner un motif de déplacement !',
])
],
'required' => true
])
->add('devisLuggage', EntityType::class, [
'label' => 'Bagages',
'attr' => [
'class' => 'chosen-select no-search-select pl-3 mb-0',
],
'placeholder' => 'Sélectionner',
'class' => DevisLuggage::class,
'choice_label' => function (DevisLuggage $bagage) {
return $bagage->getName();
},
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('dl')
->orderBy('dl.id', 'DESC');
},
'constraints' => [
new NotBlank([
'message' => 'Veuillez indiquer vos bagages !',
])
],
'required' => true
])
->add('infos', TextareaType::class, [
'label' => 'Avez-vous d\'autres informations à transmettre aux transporteurs?',
'attr' => [
'class' => 'form-control',
'cols' => '40',
'rows' => '5',
'maxlength' => '1000',
'placeholder' => 'Ne pas indiquer de coordonnées personnelles (e-mail, téléphone, nom, prénom, ...)'
],
'constraints' => [
new Length([
'max' => 1000,
'maxMessage' => 'Votre information ne doit pas dépasser {{ limit }} caractères.',
]),
],
'required' => false
])
->add('devisBudget', EntityType::class, [
'label' => 'Avez-vous une idée du budget que vous souhaitez consacrer à ce voyage?',
'attr' => [
'class' => 'chosen-select no-search-select pl-3 mb-0',
],
'placeholder' => 'Sélectionner',
'class' => DevisBudget::class,
'choice_label' => function (DevisBudget $budget) {
return $budget->getName();
},
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('db')
->orderBy('db.id', 'DESC');
},
'constraints' => [
new NotBlank([
'message' => 'Veuillez indiquer un budget !',
])
],
'required' => true
])
->add('devisEquipment', EntityType::class, [
'label' => 'Equipement spécifique indispensable?',
'class' => DevisEquipment::class,
'choice_label' => 'name',
'multiple' => true,
'expanded' => true,
'required' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('de')
->orderBy('de.id', 'DESC');
},
])
;
}
if ($options['step'] === 'step3') {
$builder
->add('miseEnAvance', CheckboxType::class, [
'label' => 'Mise en avant de votre demande : 6 Crédits',
'required' => false,
'attr' => ['class' => 'checkbox'],
])
->add('remonteEnTeteDeListe', CheckboxType::class, [
'label' => 'Remonter en tête de liste tout les jours : 8 Crédits',
'required' => false,
'attr' => ['class' => 'checkbox'],
])
->add('demandeUrgente', CheckboxType::class, [
'label' => 'Logo demande urgente : 3 Crédits',
'required' => false,
'attr' => ['class' => 'checkbox'],
])
;
}
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Devis::class,
'step' => 'step1'
]);
}
public function getBlockPrefix()
{
return 'sg_devis';
}
}