diff --git a/assets/controllers/form_partial_controller.js b/assets/controllers/form_partial_controller.js new file mode 100644 index 0000000..77170ce --- /dev/null +++ b/assets/controllers/form_partial_controller.js @@ -0,0 +1,35 @@ +import { Controller } from '@hotwired/stimulus' +import { useFetch } from '../mixins/use_fetch' + +export default class extends Controller { + + static targets = [ 'form', 'container', 'spinner' ] + static values = { url: String, loading: Boolean } + + connect() { + useFetch(this) + } + + update() { + this.loadingValue = true + const formData = new FormData(this.formTarget) + fetch(this.urlValue, { method: 'POST', body: formData }) + .then(this.checkStatus) + .then(this.parseHTML) + .then(response => { + this.containerTarget.innerHTML = response + }) + .catch(error => { + }) + .finally(() => { + this.loadingValue = false + }) + + } + + loadingValueChanged(loading) { + if (this.hasSpinnerTarget) { + this.spinnerTarget.classList.toggle('hidden', false === loading) + } + } +} \ No newline at end of file diff --git a/src/BusProNet/Model/Pickup.php b/src/BusProNet/Model/Pickup.php index cd88b05..8e84b89 100644 --- a/src/BusProNet/Model/Pickup.php +++ b/src/BusProNet/Model/Pickup.php @@ -7,8 +7,23 @@ class Pickup private ?int $id = null; private ?int $busProId = null; private ?string $code = null; - private ?string $city; - private ?string $street; + private ?string $city = null; + private ?string $street = null; + private ?string $time = null; + + public static function fromArray(array $data): static + { + $instance = new static(); + $instance + ->setId($data['busProId']) + ->setCode($data['code'] ?? null) + ->setCity($data['city']) + ->setStreet($data['street']) + ->setTime($data['time']) + ; + + return $instance; + } public function getId(): ?int { @@ -69,4 +84,16 @@ class Pickup return $this; } + + public function getTime(): ?string + { + return $this->time; + } + + public function setTime(?string $time): static + { + $this->time = $time; + + return $this; + } } \ No newline at end of file diff --git a/src/Controller/Admin/Assignment/PickupFormController.php b/src/Controller/Admin/Assignment/PickupFormController.php new file mode 100644 index 0000000..f34af0a --- /dev/null +++ b/src/Controller/Admin/Assignment/PickupFormController.php @@ -0,0 +1,28 @@ +createForm(AssignmentType::class, $assignment); + $form->handleRequest($request); + + return $this->render('admin/assignment/_form_pickup.html.twig', [ + 'form' => $form->createView(), + ]); + } +} \ No newline at end of file diff --git a/src/Entity/Destination.php b/src/Entity/Destination.php index 1ad08e5..3666145 100644 --- a/src/Entity/Destination.php +++ b/src/Entity/Destination.php @@ -2,6 +2,7 @@ namespace App\Entity; +use App\BusProNet\Model\Pickup; use App\Entity\Traits\SoftDeletableEntity; use App\Entity\Traits\TimestampableEntity; use App\Repository\DestinationRepository; @@ -171,6 +172,17 @@ class Destination implements TimestampableEntityInterface, SoftDeletableEntityIn return $this; } + public function getPickup(int $id): ?Pickup + { + foreach ($this->getPickups() as $pickup) { + if ($id === $pickup['busProId']) { + return Pickup::fromArray($pickup); + } + } + + return null; + } + public function hasBusTransfer(): bool { return 0 < count($this->getPickups()); diff --git a/src/Entity/Disposition.php b/src/Entity/Disposition.php index 040ffcd..5f9af72 100644 --- a/src/Entity/Disposition.php +++ b/src/Entity/Disposition.php @@ -43,7 +43,7 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf private ?Teamer $teamer; #[ORM\Column(nullable: true)] - private ?int $pickup = null; + private ?int $pickup; #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $remarks; diff --git a/src/Form/AssignmentType.php b/src/Form/AssignmentType.php index 557d5b9..8f1b84b 100644 --- a/src/Form/AssignmentType.php +++ b/src/Form/AssignmentType.php @@ -8,18 +8,27 @@ use App\Entity\Fee; use App\Entity\JobProfile; use App\Entity\Upload; use App\Entity\User; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class AssignmentType extends AbstractType { + public function __construct(private readonly EntityManagerInterface $entityManager) + { + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder @@ -37,6 +46,7 @@ class AssignmentType extends AbstractType 'label_property' => 'product', 'label_function' => fn(Destination $destination) => (string) $destination, 'endpoint_route' => 'app_admin_autocomplete_destination', + 'controller_action' => 'select->form-partial#update', ]) ->add('jobProfile', EntityType::class, [ 'label' => 'Jobprofil', @@ -60,11 +70,6 @@ class AssignmentType extends AbstractType 'label' => 'abweichender Zustieg bei Busbegleitung', 'required' => false, ]) - ->add('pickup', BpnPickupType::class, [ - 'label' => 'Busbegleitung ab', - 'required' => false, - 'placeholder' => 'Keine Busbegleitung', - ]) ->add('benefits', TextareaType::class, [ 'label' => 'Benefits', 'required' => false, @@ -133,9 +138,62 @@ class AssignmentType extends AbstractType 'rows' => 3, ], ]) + ->add('pickup', ChoiceType::class, [ + 'label' => 'Busbegleitung ab', + 'required' => false, + 'placeholder' => 'Keine Busbegleitung', + ]) + ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $form = $event->getForm(); + $data = $event->getData(); + $destination = $data->getDestination(); + + if (null === $destination) { + return; + } + + $this->addPickupField($destination, $form); + }) + ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { + $form = $event->getForm(); + $data = $event->getData(); + $destinationId = $data['destination'] ?? null; + + if (null === $destinationId) { + return; + } + + $destination = $this + ->entityManager + ->getRepository(Destination::class) + ->find($destinationId) + ; + + $this->addPickupField($destination, $form); + }) ; } + private function addPickupField(Destination $destination, FormInterface $form): void + { + $choices = []; + + foreach ($destination->getPickups() as $pickup) { + $label = sprintf('%s, %s Uhr', trim($pickup['city']), trim($pickup['time'])); + $choices[$label] = $pickup['busProId']; + } + + // Remove placeholder field first + $form->remove('pickup'); + + $form->add('pickup', ChoiceType::class, [ + 'label' => 'Busbegleitung ab', + 'required' => false, + 'placeholder' => 'Keine Busbegleitung', + 'choices' => $choices, + ]); + } + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ diff --git a/src/Twig/AppRuntime.php b/src/Twig/AppRuntime.php index eb44806..80cb8e5 100644 --- a/src/Twig/AppRuntime.php +++ b/src/Twig/AppRuntime.php @@ -50,7 +50,7 @@ class AppRuntime implements RuntimeExtensionInterface return 'nationality' === $property ? $country->getNationality() : $country->getName(); } - public function bpnPickupLabel(?int $id): string + public function bpnPickupLabel(?int $id): ?string { if (null === $id) { return 'keine Busbegleitung'; @@ -59,7 +59,7 @@ class AppRuntime implements RuntimeExtensionInterface $pickup = $this->pickups->get($id); if (null === $pickup) { - return 'unbekannt'; + return null; } return 'ab '.$pickup->getCity(); diff --git a/templates/_partials/_assignment_info.html.twig b/templates/_partials/_assignment_info.html.twig index dd1ff89..26f3215 100644 --- a/templates/_partials/_assignment_info.html.twig +++ b/templates/_partials/_assignment_info.html.twig @@ -24,29 +24,28 @@ {% if assignment.pickup %} + {% set pickup = assignment.destination.pickup(assignment.pickup) %}