diff --git a/assets/controllers/datepicker_controller.js b/assets/controllers/datepicker_controller.js index dfbabb3..0440bda 100644 --- a/assets/controllers/datepicker_controller.js +++ b/assets/controllers/datepicker_controller.js @@ -6,26 +6,25 @@ import { German } from 'flatpickr/dist/l10n/de'; export default class extends Controller { static targets = [ 'field' ] - static values = { format: String, minDate: String, maxDate: String, disableWeekends: Boolean } + static values = { + minDate: String, + maxDate: String, + disableWeekends: Boolean, + } initialize() { - flatpickr.localize(German); - flatpickr.setDefaults({ - dateFormat: 'd.m.Y', - allowInput: true - }); + flatpickr.localize(German) } connect() { - const dateFormat = this.formatValue || 'd.m.Y' const minDate = this.minDateValue const maxDate = this.maxDateValue const options = { dateFormat: 'Y-m-d', - altInput: true, - altFormat: dateFormat, + altFormat: 'd.m.Y', minDate, maxDate, + altInput: true, allowInput: true, onClose(dates, currentdatestring, instance) { instance.setDate(instance.altInput.value, true, instance.config.altFormat) diff --git a/config/services.yaml b/config/services.yaml index fb25930..dc0daa2 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -72,14 +72,6 @@ services: method: createTeamerMenu alias: admin_teamer - App\Menu\ManagerMenuBuilder: - arguments: - $factory: '@knp_menu.factory' - tags: - - name: knp_menu.menu_builder - method: createMainMenu - alias: manager_main - App\Menu\TeamerMenuBuilder: arguments: $factory: '@knp_menu.factory' diff --git a/migrations/Version20231007143035.php b/migrations/Version20231007143035.php new file mode 100644 index 0000000..2b2ed46 --- /dev/null +++ b/migrations/Version20231007143035.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE assignment CHANGE date_from date_from DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', CHANGE date_to date_to DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', CHANGE destination_bus_pro_id destination_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE assignment ADD CONSTRAINT FK_30C544BA816C6140 FOREIGN KEY (destination_id) REFERENCES bpn_date (id)'); + $this->addSql('CREATE INDEX IDX_30C544BA816C6140 ON assignment (destination_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment DROP FOREIGN KEY FK_30C544BA816C6140'); + $this->addSql('DROP INDEX IDX_30C544BA816C6140 ON assignment'); + $this->addSql('ALTER TABLE assignment CHANGE date_from date_from DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', CHANGE date_to date_to DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', CHANGE destination_id destination_bus_pro_id INT DEFAULT NULL'); + } +} diff --git a/migrations/Version20231007144753.php b/migrations/Version20231007144753.php new file mode 100644 index 0000000..13a643c --- /dev/null +++ b/migrations/Version20231007144753.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE assignment ADD pickup_date_and_time DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', DROP pickup_date'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment ADD pickup_date DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', DROP pickup_date_and_time'); + } +} diff --git a/migrations/Version20231007145904.php b/migrations/Version20231007145904.php new file mode 100644 index 0000000..a8a3e88 --- /dev/null +++ b/migrations/Version20231007145904.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE assignment ADD pickup_date DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', DROP pickup_date_and_time'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment ADD pickup_date_and_time DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', DROP pickup_date'); + } +} diff --git a/migrations/Version20231007150056.php b/migrations/Version20231007150056.php new file mode 100644 index 0000000..a0baa61 --- /dev/null +++ b/migrations/Version20231007150056.php @@ -0,0 +1,41 @@ +addSql('CREATE TABLE job_profile_training (job_profile_id INT NOT NULL, training_id INT NOT NULL, INDEX IDX_F19E0BB8A68D82A5 (job_profile_id), INDEX IDX_F19E0BB8BEFD98D1 (training_id), PRIMARY KEY(job_profile_id, training_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE job_profile_training ADD CONSTRAINT FK_F19E0BB8A68D82A5 FOREIGN KEY (job_profile_id) REFERENCES job_profile (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE job_profile_training ADD CONSTRAINT FK_F19E0BB8BEFD98D1 FOREIGN KEY (training_id) REFERENCES training (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE job_profile DROP FOREIGN KEY FK_1BB81D94FEBEF441'); + $this->addSql('DROP INDEX IDX_1BB81D94FEBEF441 ON job_profile'); + $this->addSql('ALTER TABLE job_profile DROP required_training_id'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE job_profile_training DROP FOREIGN KEY FK_F19E0BB8A68D82A5'); + $this->addSql('ALTER TABLE job_profile_training DROP FOREIGN KEY FK_F19E0BB8BEFD98D1'); + $this->addSql('DROP TABLE job_profile_training'); + $this->addSql('ALTER TABLE job_profile ADD required_training_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE job_profile ADD CONSTRAINT FK_1BB81D94FEBEF441 FOREIGN KEY (required_training_id) REFERENCES training (id)'); + $this->addSql('CREATE INDEX IDX_1BB81D94FEBEF441 ON job_profile (required_training_id)'); + } +} diff --git a/migrations/Version20231007151506.php b/migrations/Version20231007151506.php new file mode 100644 index 0000000..44122a8 --- /dev/null +++ b/migrations/Version20231007151506.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE assignment CHANGE pickup_date pickup_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment CHANGE pickup_date pickup_date DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\''); + } +} diff --git a/src/Controller/Admin/Application/IndexController.php b/src/Controller/Admin/Application/IndexController.php index 8750303..83d478e 100644 --- a/src/Controller/Admin/Application/IndexController.php +++ b/src/Controller/Admin/Application/IndexController.php @@ -5,10 +5,12 @@ namespace App\Controller\Admin\Application; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; class IndexController extends AbstractController { #[Route('/admin/application', name: 'app_admin_application_index')] + #[IsGranted('ROLE_ADMINISTRATIVE')] public function index(): Response { return $this->render('admin/application/index.html.twig'); diff --git a/src/Controller/Admin/Document/IndexController.php b/src/Controller/Admin/Document/IndexController.php index 63bbae9..4b4aa18 100644 --- a/src/Controller/Admin/Document/IndexController.php +++ b/src/Controller/Admin/Document/IndexController.php @@ -5,10 +5,12 @@ namespace App\Controller\Admin\Document; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; class IndexController extends AbstractController { #[Route('/admin/document', name: 'app_admin_document_index')] + #[IsGranted('ROLE_ADMINISTRATIVE')] public function index(): Response { return $this->render('admin/document/index.html.twig'); diff --git a/src/Controller/Admin/IndexController.php b/src/Controller/Admin/IndexController.php index b976336..bb7a9a5 100644 --- a/src/Controller/Admin/IndexController.php +++ b/src/Controller/Admin/IndexController.php @@ -10,7 +10,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted; class IndexController extends AbstractController { #[Route('/admin', name: 'app_admin_index')] - #[IsGranted('ROLE_ADMIN')] + #[IsGranted('ROLE_ADMINISTRATIVE')] public function index(): Response { return $this->render('admin/index.html.twig'); diff --git a/src/Controller/Admin/Teamer/IndexController.php b/src/Controller/Admin/Teamer/IndexController.php index e2f022c..34c0670 100644 --- a/src/Controller/Admin/Teamer/IndexController.php +++ b/src/Controller/Admin/Teamer/IndexController.php @@ -19,7 +19,7 @@ class IndexController extends AbstractController } #[Route('/admin/teamer', name: 'app_admin_teamer_index')] - #[IsGranted('ROLE_ADMIN')] + #[IsGranted('ROLE_ADMINISTRATIVE')] public function index(Request $request): Response { $query = $this diff --git a/src/Controller/Common/Assignment/CreateController.php b/src/Controller/Common/Assignment/CreateController.php deleted file mode 100644 index 5ef287f..0000000 --- a/src/Controller/Common/Assignment/CreateController.php +++ /dev/null @@ -1,19 +0,0 @@ -render('manager/index.html.twig'); - } -} \ No newline at end of file diff --git a/src/Controller/Teamer/Profile/SkillsController.php b/src/Controller/Teamer/Profile/SkillsController.php index d3f1154..fe5366d 100644 --- a/src/Controller/Teamer/Profile/SkillsController.php +++ b/src/Controller/Teamer/Profile/SkillsController.php @@ -2,6 +2,7 @@ namespace App\Controller\Teamer\Profile; +use App\Entity\JobProfile; use App\Entity\User; use App\Form\TeamerJobProfileType; use App\Repository\JobProfileRepository; @@ -34,15 +35,21 @@ class SkillsController extends AbstractController $selectableJobProfiles = []; foreach ($jobProfiles as $profile) { - $selectableJobProfiles[$profile->getId()] = true; - if (null !== $profile->getRequiredTraining() && null === $teamer->getTrainingAttendance($profile->getRequiredTraining())) { - $selectableJobProfiles[$profile->getId()] = false; - } - foreach ($profile->getRequiredLicenses() as $license) { - if (null === $teamer->getLicenseOfType($license)) { - $selectableJobProfiles[$profile->getId()] = false; + /** @var JobProfile $profile */ + $jobProfileIsSelectable = false; + foreach ($profile->getRequiredTrainings() as $training) { + if (null !== $teamer->getTrainingAttendance($training)) { + $jobProfileIsSelectable = true; + break; } } + foreach ($profile->getRequiredLicenses() as $license) { + if (null !== $teamer->getLicenseOfType($license)) { + $jobProfileIsSelectable = true; + break; + } + } + $selectableJobProfiles[$profile->getId()] = $jobProfileIsSelectable; } $form = $this->createForm(TeamerJobProfileType::class, $teamer, ['job_profiles' => $jobProfiles, 'selectable_job_profiles' => $selectableJobProfiles]); diff --git a/src/Entity/Assignment.php b/src/Entity/Assignment.php index 2978ac8..5774728 100644 --- a/src/Entity/Assignment.php +++ b/src/Entity/Assignment.php @@ -12,6 +12,7 @@ use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Uid\Uuid; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Context\ExecutionContextInterface; #[ORM\Entity(repositoryClass: AssignmentRepository::class)] class Assignment implements BlameableEntityInterface, TimestampableEntityInterface @@ -39,17 +40,14 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa #[Assert\Range(min: 1, minMessage: 'Die Mindestanzahl ist 1')] private ?int $available = 1; - #[ORM\Column(type: Types::DATE_IMMUTABLE)] - #[Assert\NotNull(message: 'Bitte gib das Beginndatum an')] + #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $dateFrom = null; - #[ORM\Column(type: Types::DATE_IMMUTABLE)] - #[Assert\NotNull(message: 'Bitte gib das Enddatum an')] + #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] #[Assert\GreaterThan(propertyPath: 'dateFrom', message: 'Das Enddatum muss nach dem Beginndatum liegen')] private ?\DateTimeImmutable $dateTo = null; - #[ORM\Column(type: Types::DATE_IMMUTABLE)] - #[Assert\GreaterThanOrEqual(propertyPath: 'dateFrom', message: 'Die Busabfahrt muss am oder nach dem Beginndatum liegen')] + #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $pickupDate = null; #[ORM\Column(nullable: true)] diff --git a/src/Entity/BpnDate.php b/src/Entity/BpnDate.php index af910c4..f2b63ee 100644 --- a/src/Entity/BpnDate.php +++ b/src/Entity/BpnDate.php @@ -49,6 +49,16 @@ class BpnDate $this->bus = false; } + public function __toString(): string + { + return sprintf('%s - %s: %s, %s', + $this->getDateFrom()->format('d.m.y'), + $this->getDateTo()->format('d.m.y'), + $this->getProduct(), + $this->getHotel() + ); + } + public function getId(): ?int { return $this->id; diff --git a/src/Entity/JobProfile.php b/src/Entity/JobProfile.php index 6746812..f1002a7 100644 --- a/src/Entity/JobProfile.php +++ b/src/Entity/JobProfile.php @@ -5,6 +5,8 @@ namespace App\Entity; use App\Entity\Traits\BlameableEntity; use App\Entity\Traits\TimestampableEntity; use App\Repository\JobProfileRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Uid\Uuid; @@ -28,15 +30,16 @@ class JobProfile implements BlameableEntityInterface, TimestampableEntityInterfa #[Assert\NotBlank(message: 'Bitte gib die Bezeichnung an')] private ?string $name = null; - #[ORM\ManyToOne] - private ?Training $requiredTraining = null; - #[ORM\Column(type: Types::JSON)] private array $requiredLicenses = []; + #[ORM\ManyToMany(targetEntity: Training::class)] + private Collection $requiredTrainings; + public function __construct() { $this->uuid = Uuid::v4(); + $this->requiredTrainings = new ArrayCollection(); } public function getId(): ?int @@ -61,18 +64,6 @@ class JobProfile implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } - public function getRequiredTraining(): ?Training - { - return $this->requiredTraining; - } - - public function setRequiredTraining(?Training $requiredTraining): static - { - $this->requiredTraining = $requiredTraining; - - return $this; - } - public function getRequiredLicenses(): array { return $this->requiredLicenses; @@ -84,4 +75,28 @@ class JobProfile implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } + + /** + * @return Collection + */ + public function getRequiredTrainings(): Collection + { + return $this->requiredTrainings; + } + + public function addRequiredTraining(Training $requiredTraining): static + { + if (!$this->requiredTrainings->contains($requiredTraining)) { + $this->requiredTrainings->add($requiredTraining); + } + + return $this; + } + + public function removeRequiredTraining(Training $requiredTraining): static + { + $this->requiredTrainings->removeElement($requiredTraining); + + return $this; + } } diff --git a/src/Form/AssignmentType.php b/src/Form/AssignmentType.php index 0407247..b32ff7e 100644 --- a/src/Form/AssignmentType.php +++ b/src/Form/AssignmentType.php @@ -30,6 +30,9 @@ class AssignmentType extends AbstractType 'label' => 'Destination', 'class' => BpnDate::class, 'label_property' => 'product', + 'label_function' => function (BpnDate $date) { + return (string) $date; + }, 'endpoint_route' => 'app_admin_autocomplete_bpndate', ]) ->add('jobProfile', EntityType::class, [ @@ -39,10 +42,12 @@ class AssignmentType extends AbstractType 'placeholder' => 'Bitte auswählen', ]) ->add('dateFrom', DatepickerType::class, [ - 'label' => 'Einsatztermin von', + 'label' => 'abweichender Einsatztermin von', + 'required' => false, ]) ->add('dateTo', DatepickerType::class, [ - 'label' => 'Einsatztermin bis', + 'label' => 'abweichender Einsatztermin bis', + 'required' => false, ]) ->add('pickupDate', DatepickerType::class, [ 'label' => 'Busabfahrt', diff --git a/src/Form/JobProfileType.php b/src/Form/JobProfileType.php index e66af1a..518acfd 100644 --- a/src/Form/JobProfileType.php +++ b/src/Form/JobProfileType.php @@ -21,12 +21,14 @@ class JobProfileType extends AbstractType ->add('name', TextType::class, [ 'label' => 'Bezeichnung', ]) - ->add('requiredTraining', EntityType::class, [ + ->add('requiredTrainings', EntityType::class, [ 'label' => 'vorausgesetzte Fortbildung', 'required' => false, 'placeholder' => 'keine', 'class' => Training::class, 'choice_label' => 'name', + 'multiple' => true, + 'expanded' => true, 'query_builder' => function (EntityRepository $repository) { return $repository->createQueryBuilder('job_profile') ->orderBy('job_profile.name', 'ASC') diff --git a/src/Form/TeamerJobProfileType.php b/src/Form/TeamerJobProfileType.php index 96dde73..46d4960 100644 --- a/src/Form/TeamerJobProfileType.php +++ b/src/Form/TeamerJobProfileType.php @@ -4,6 +4,7 @@ namespace App\Form; use App\Entity\JobProfile; use App\Entity\Teamer; +use App\Entity\Training; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -23,10 +24,11 @@ class TeamerJobProfileType extends AbstractType 'class' => JobProfile::class, // Append potential training or license requirements to profile labels 'choice_label' => function($choice, string $key, mixed $value): TranslatableMessage|string { + /** @var JobProfile $choice */ $label = $choice->getName(); $requirements = []; - if (null !== $training = $choice->getRequiredTraining()) { - $requirements[].= $training->getName(); + foreach ($choice->getRequiredTrainings() as $training) { + $requirements[] = $training->getName(); } foreach ($choice->getRequiredLicenses() as $license) { $requirements[] = sprintf('Offizielle %slehrer*innenlizenz', ucfirst(strtolower($license))); diff --git a/src/Menu/AbstractMenuBuilder.php b/src/Menu/AbstractMenuBuilder.php index ebb0962..7c4808a 100644 --- a/src/Menu/AbstractMenuBuilder.php +++ b/src/Menu/AbstractMenuBuilder.php @@ -88,17 +88,6 @@ abstract class AbstractMenuBuilder } } - protected function addManagerItem(ItemInterface $menu): void - { - if ($this->security->isGranted('ROLE_MANAGER')) { - $menu - ->addChild('Managerbereich', ['route' => 'app_manager_index']) - ->setChildrenAttribute('title', 'Managerbereich') - ->setExtra('icon', 'user') - ; - } - } - protected function addTeamerItem(ItemInterface $menu): void { if ($this->security->isGranted('ROLE_TEAMER')) { diff --git a/src/Menu/AdminMenuBuilder.php b/src/Menu/AdminMenuBuilder.php index 99c8e80..2365f02 100644 --- a/src/Menu/AdminMenuBuilder.php +++ b/src/Menu/AdminMenuBuilder.php @@ -125,7 +125,6 @@ class AdminMenuBuilder extends AbstractMenuBuilder $this->addDivider($menu); - $this->addManagerItem($menu); $this->addTeamerItem($menu); $this->addDivider($menu); diff --git a/src/Menu/ManagerMenuBuilder.php b/src/Menu/ManagerMenuBuilder.php deleted file mode 100644 index a249118..0000000 --- a/src/Menu/ManagerMenuBuilder.php +++ /dev/null @@ -1,22 +0,0 @@ -createRootElement(); - - $this->addAdminItem($menu); - $this->addTeamerItem($menu); - - $this->addDivider($menu); - - $this->addLogoutItem($menu); - - return $menu; - } -} \ No newline at end of file diff --git a/src/Menu/TeamerMenuBuilder.php b/src/Menu/TeamerMenuBuilder.php index ecbc760..e11451e 100644 --- a/src/Menu/TeamerMenuBuilder.php +++ b/src/Menu/TeamerMenuBuilder.php @@ -86,7 +86,6 @@ class TeamerMenuBuilder extends AbstractMenuBuilder $this->addDivider($menu); $this->addAdminItem($menu); - $this->addManagerItem($menu); $this->addDivider($menu); diff --git a/src/Repository/BpnDateRepository.php b/src/Repository/BpnDateRepository.php index 4c16062..62c14e8 100644 --- a/src/Repository/BpnDateRepository.php +++ b/src/Repository/BpnDateRepository.php @@ -29,17 +29,16 @@ class BpnDateRepository extends ServiceEntityRepository $qb = $this->createQueryBuilder('bpn_date'); $dates = $qb - ->select('bpn_date.id', 'bpn_date.product', 'bpn_date.hotel', 'bpn_date.dateFrom', 'bpn_date.dateTo') ->where($qb->expr()->orX( $qb->expr()->like('bpn_date.product', ':product'), $qb->expr()->like('bpn_date.hotel', ':hotel') )) ->setParameter('product', '%'.$this->escapeLikeWildcards($search).'%') ->setParameter('hotel', '%'.$this->escapeLikeWildcards($search).'%') - ->orderBy('bpn_date.product', 'ASC') - ->addOrderBy('bpn_date.dateFrom', 'ASC') + ->orderBy('bpn_date.dateFrom', 'ASC') + ->addOrderBy('bpn_date.product', 'ASC') ->getQuery() - ->getArrayResult() + ->getResult() ; $data = [ @@ -51,8 +50,8 @@ class BpnDateRepository extends ServiceEntityRepository foreach ($dates as $date) { $data[] = [ - 'value' => $date['id'], - 'text' => sprintf('%s (%s - %s)', $date['product'], $date['dateFrom']->format('d.m.y'), $date['dateTo']->format('d.m.y')), + 'value' => $date->getId(), + 'text' => (string) $date, ]; } diff --git a/src/Repository/JobProfileRepository.php b/src/Repository/JobProfileRepository.php index cb7b4d4..2f8e180 100644 --- a/src/Repository/JobProfileRepository.php +++ b/src/Repository/JobProfileRepository.php @@ -27,7 +27,7 @@ class JobProfileRepository extends ServiceEntityRepository return $qb ->select('job_profile', 'required_training') - ->leftJoin('job_profile.requiredTraining', 'required_training') + ->leftJoin('job_profile.requiredTrainings', 'required_training') ->orderBy('job_profile.name', 'ASC') ->getQuery() ->getResult() diff --git a/templates/admin/assignment/_form.html.twig b/templates/admin/assignment/_form.html.twig index 265e910..797913d 100644 --- a/templates/admin/assignment/_form.html.twig +++ b/templates/admin/assignment/_form.html.twig @@ -1,17 +1,19 @@ {{ form_start(form) }}
-
+
+
+ {{ form_row(form.destination) }} +
{{ form_row(form.available) }} - {{ form_row(form.jobProfile) }} -
-
- {{ form_row(form.destination) }} - {{ form_row(form.contact) }}
{{ form_row(form.dateFrom) }} {{ form_row(form.dateTo) }}
+
+ {{ form_row(form.jobProfile) }} + {{ form_row(form.contact) }} +
{{ form_row(form.pickupDate) }} {{ form_row(form.pickup) }} @@ -20,7 +22,9 @@ {{ form_row(form.benefits) }} {{ form_row(form.fees) }}
- {{ form_row(form.remarks) }} +
+ {{ form_row(form.remarks) }} +