diff --git a/assets/controllers/autocomplete_controller.js b/assets/controllers/autocomplete_controller.js index c675e0a..c4d48f7 100644 --- a/assets/controllers/autocomplete_controller.js +++ b/assets/controllers/autocomplete_controller.js @@ -49,6 +49,7 @@ export default class extends Controller { new Autocomplete(this.element, { search: searchHandler, getResultValue: result => result.text, + submitOnEnter: true, onSubmit: submitHandler, debounceTime: 250, }) diff --git a/assets/controllers/datepicker_controller.js b/assets/controllers/datepicker_controller.js index dfbabb3..fe36b28 100644 --- a/assets/controllers/datepicker_controller.js +++ b/assets/controllers/datepicker_controller.js @@ -6,7 +6,7 @@ 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 = { format: String, minDate: String, maxDate: String, disableWeekends: Boolean, mode: String } initialize() { flatpickr.localize(German); @@ -17,10 +17,12 @@ export default class extends Controller { } connect() { + const mode = this.modeValue || 'single' const dateFormat = this.formatValue || 'd.m.Y' const minDate = this.minDateValue const maxDate = this.maxDateValue const options = { + mode, dateFormat: 'Y-m-d', altInput: true, altFormat: dateFormat, diff --git a/migrations/Version20231004134943.php b/migrations/Version20231004134943.php new file mode 100644 index 0000000..05463c0 --- /dev/null +++ b/migrations/Version20231004134943.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE assignment ADD job_profile_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE assignment ADD CONSTRAINT FK_30C544BAA68D82A5 FOREIGN KEY (job_profile_id) REFERENCES job_profile (id)'); + $this->addSql('CREATE INDEX IDX_30C544BAA68D82A5 ON assignment (job_profile_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_30C544BAA68D82A5'); + $this->addSql('DROP INDEX IDX_30C544BAA68D82A5 ON assignment'); + $this->addSql('ALTER TABLE assignment DROP job_profile_id'); + } +} diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php index aff726f..3f230a5 100644 --- a/src/BusProNet/ApiClient.php +++ b/src/BusProNet/ApiClient.php @@ -16,8 +16,10 @@ class ApiClient { public const TYPE_NOTIFICATION = 'HINWEIS'; public const TYPE_CUSTOMER_DATA = 'KUNDENKONTO'; + public const TYPE_PRODUCTS = 'PRODUKTE'; public const TYPE_BASE_DATA_COUNTRIES = 'STAMMLAENDER'; public const TYPE_BASE_DATA_PICKUPS = 'STAMMZUSTIEGE'; + public const TYPE_BASE_DATA_HOTELS = 'STAMMHOTELS'; private array $config; diff --git a/src/BusProNet/DataProvider/Countries.php b/src/BusProNet/DataProvider/CountryDataProvider.php similarity index 97% rename from src/BusProNet/DataProvider/Countries.php rename to src/BusProNet/DataProvider/CountryDataProvider.php index cc56e80..4f884c4 100644 --- a/src/BusProNet/DataProvider/Countries.php +++ b/src/BusProNet/DataProvider/CountryDataProvider.php @@ -8,7 +8,7 @@ use App\BusProNet\Model\Country; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; -class Countries +class CountryDataProvider { public function __construct(private readonly ApiClient $apiClient, private readonly CacheInterface $cache) {} diff --git a/src/BusProNet/DataProvider/HotelDataProvider.php b/src/BusProNet/DataProvider/HotelDataProvider.php new file mode 100644 index 0000000..a7045af --- /dev/null +++ b/src/BusProNet/DataProvider/HotelDataProvider.php @@ -0,0 +1,39 @@ +cache->get('bpn_hotels', function (ItemInterface $item) { + $item->expiresAfter(3600); + + return $this + ->apiClient + ->getBaseData(ApiClient::TYPE_BASE_DATA_HOTELS) + ->getItems() + ; + }); + } catch (ApiClientException $e) { + $hotels = []; + } + + return $hotels; + } + + public function get(int $busProId): ?Hotel + { + return $this->getAll()[$busProId] ?? null; + } +} \ No newline at end of file diff --git a/src/BusProNet/DataProvider/Pickups.php b/src/BusProNet/DataProvider/PickupDataProvider.php similarity index 97% rename from src/BusProNet/DataProvider/Pickups.php rename to src/BusProNet/DataProvider/PickupDataProvider.php index 9eb970d..75eaebc 100644 --- a/src/BusProNet/DataProvider/Pickups.php +++ b/src/BusProNet/DataProvider/PickupDataProvider.php @@ -8,7 +8,7 @@ use App\BusProNet\Model\Pickup; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; -class Pickups +class PickupDataProvider { public function __construct(private readonly ApiClient $apiClient, private readonly CacheInterface $cache) {} diff --git a/src/BusProNet/DataProvider/ProductDataProvider.php b/src/BusProNet/DataProvider/ProductDataProvider.php new file mode 100644 index 0000000..b694f6c --- /dev/null +++ b/src/BusProNet/DataProvider/ProductDataProvider.php @@ -0,0 +1,39 @@ +cache->get('bpn_products', function (ItemInterface $item) { + $item->expiresAfter(3600); + + return $this + ->apiClient + ->getBaseData(ApiClient::TYPE_PRODUCTS) + ->getItems() + ; + }); + } catch (ApiClientException $e) { + $products = []; + } + + return $products; + } + + public function get(int $busProId): ?Product + { + return $this->getAll()[$busProId] ?? null; + } +} \ No newline at end of file diff --git a/src/BusProNet/Model/BaseDataResponse.php b/src/BusProNet/Model/BaseDataResponse.php index d2c0d65..8820512 100644 --- a/src/BusProNet/Model/BaseDataResponse.php +++ b/src/BusProNet/Model/BaseDataResponse.php @@ -4,17 +4,11 @@ namespace App\BusProNet\Model; class BaseDataResponse { - private array $items = []; + public function __construct(private readonly array $items) + {} public function getItems(): array { return $this->items; } - - public function setItems(array $items): static - { - $this->items = $items; - - return $this; - } } \ No newline at end of file diff --git a/src/BusProNet/Model/Hotel.php b/src/BusProNet/Model/Hotel.php new file mode 100644 index 0000000..3729150 --- /dev/null +++ b/src/BusProNet/Model/Hotel.php @@ -0,0 +1,112 @@ +id; + } + + public function setId(?int $id): static + { + $this->id = $id; + + return $this; + } + + public function getBusProId(): ?int + { + return $this->busProId; + } + + public function setBusProId(?int $busProId): static + { + $this->busProId = $busProId; + + return $this; + } + + public function getCode(): ?string + { + return $this->code; + } + + public function setCode(?string $code): static + { + $this->code = $code; + + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): static + { + $this->name = $name; + + return $this; + } + + public function getCity(): ?string + { + return $this->city; + } + + public function setCity(?string $city): static + { + $this->city = $city; + + return $this; + } + + public function getStreet(): ?string + { + return $this->street; + } + + public function setStreet(?string $street): static + { + $this->street = $street; + + return $this; + } + + public function getPhone(): ?string + { + return $this->phone; + } + + public function setPhone(?string $phone): static + { + $this->phone = $phone; + + return $this; + } + + public function getType(): ?string + { + return $this->type; + } + + public function setType(?string $type): static + { + $this->type = $type; + + return $this; + } + +} \ No newline at end of file diff --git a/src/BusProNet/Model/Product.php b/src/BusProNet/Model/Product.php new file mode 100644 index 0000000..f7e6c91 --- /dev/null +++ b/src/BusProNet/Model/Product.php @@ -0,0 +1,46 @@ +id; + } + + public function setId(?int $id): static + { + $this->id = $id; + + return $this; + } + + public function getCode(): ?string + { + return $this->code; + } + + public function setCode(?string $code): static + { + $this->code = $code; + + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): static + { + $this->name = $name; + + return $this; + } +} \ No newline at end of file diff --git a/src/BusProNet/ResponseParser.php b/src/BusProNet/ResponseParser.php index 62a41ab..a5c5c3e 100644 --- a/src/BusProNet/ResponseParser.php +++ b/src/BusProNet/ResponseParser.php @@ -4,13 +4,15 @@ namespace App\BusProNet; use App\BusProNet\Model\Address; use App\BusProNet\Model\BaseDataResponse; -use App\BusProNet\Model\NotificationResponse; use App\BusProNet\Model\Communication; use App\BusProNet\Model\Country; use App\BusProNet\Model\CrmAttribute; use App\BusProNet\Model\CrmAttributeGroup; use App\BusProNet\Model\CrmAttributesResponse; +use App\BusProNet\Model\Hotel; +use App\BusProNet\Model\NotificationResponse; use App\BusProNet\Model\Pickup; +use App\BusProNet\Model\Product; use App\BusProNet\Model\ProfileResponse; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -55,6 +57,10 @@ class ResponseParser return $this->createCountriesResponse($xml); case ApiClient::TYPE_BASE_DATA_PICKUPS: return $this->createPickupsResponse($xml); + case ApiClient::TYPE_BASE_DATA_HOTELS: + return $this->createHotelsResponse($xml); + case ApiClient::TYPE_PRODUCTS: + return $this->createProductsResponse($xml); } throw new ResponseParserException('Unable to parse XML response'); @@ -187,10 +193,7 @@ class ResponseParser $countries[$token] = $country; } - $response = new BaseDataResponse(); - $response->setItems($countries); - - return $response; + return new BaseDataResponse($countries); } public function createPickupsResponse(\SimpleXMLElement $xml): BaseDataResponse @@ -211,10 +214,53 @@ class ResponseParser $pickups[$busProId] = $pickup; } - $response = new BaseDataResponse(); - $response->setItems($pickups); + return new BaseDataResponse($pickups); + } - return $response; + public function createHotelsResponse(\SimpleXMLElement $xml): BaseDataResponse + { + $hotels = []; + + foreach ($xml->xpath('hotel') as $item) { + $id = (int) $item->attributes()['id']; + $busProId = (int) $item->attributes()['idbuspro']; + $hotel = new Hotel(); + $hotel + ->setId($id) + ->setBusProId($busProId) + ->setCode((string) $item->attributes()['code']) + ->setName($item->xpath('name') ? (string) $item->xpath('name')[0] : null) + ->setCity($item->xpath('ort') ? (string) $item->xpath('ort')[0] : null) + ->setStreet($item->xpath('strasse') ? (string) $item->xpath('strasse')[0] : null) + ->setPhone($item->xpath('telefon') ? (string) $item->xpath('telefon')[0] : null) + ->setType($item->xpath('art') ? (string) $item->xpath('art')[0] : null) + ; + $hotels[$busProId] = $hotel; + } + + return new BaseDataResponse($hotels); + } + + public function createProductsResponse(\SimpleXMLElement $xml): BaseDataResponse + { + $products = []; + + foreach ($xml->xpath('produkte/produkt') as $item) { + $code = (string) $item->attributes()['code']; + if (true === empty($code)) { + continue; + } + $id = (int) $item->attributes()['id']; + $product = new Product(); + $product + ->setId($id) + ->setName((string) $item->attributes()['bezeichnung']) + ->setCode($code) + ; + $products[$id] = $product; + } + + return new BaseDataResponse($products); } private function resolveOptions(array $options): array diff --git a/src/Controller/Admin/Assignment/CreateController.php b/src/Controller/Admin/Assignment/CreateController.php new file mode 100644 index 0000000..dc6e11e --- /dev/null +++ b/src/Controller/Admin/Assignment/CreateController.php @@ -0,0 +1,29 @@ +createForm(AssignmentType::class, $assignment); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + return $this->redirectToRoute('app_admin_assignment_index'); + } + + return $this->render('admin/assignment/create.html.twig', [ + 'form' => $form, + ]); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/Autocomplete/HotelController.php b/src/Controller/Admin/Autocomplete/HotelController.php new file mode 100644 index 0000000..4c4eca7 --- /dev/null +++ b/src/Controller/Admin/Autocomplete/HotelController.php @@ -0,0 +1,43 @@ +getContent(), true, 512, JSON_THROW_ON_ERROR); + $queryString = $query['search']; + } catch (\JsonException $e) { + throw $this->createNotFoundException(); + } + + $hotels = $this->hotelDataProvider->getAll(); + $data = []; + + foreach ($hotels as $hotel) { + $name = $hotel->getName(); + if (1 === preg_match('/'.preg_quote($queryString, '/').'/i', $name)) { + $data[] = [ + 'value' => $hotel->getId(), + 'text' => sprintf('%s (%s)', $name, $hotel->getCode()), + ]; + } + } + + return $this->json($data); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/Autocomplete/ProductController.php b/src/Controller/Admin/Autocomplete/ProductController.php new file mode 100644 index 0000000..e131ddf --- /dev/null +++ b/src/Controller/Admin/Autocomplete/ProductController.php @@ -0,0 +1,43 @@ +getContent(), true, 512, JSON_THROW_ON_ERROR); + $queryString = $query['search']; + } catch (\JsonException $e) { + throw $this->createNotFoundException(); + } + + $products = $this->productDataProvider->getAll(); + $data = []; + + foreach ($products as $product) { + $name = $product->getName(); + if (1 === preg_match('/'.preg_quote($queryString, '/').'/i', $name)) { + $data[] = [ + 'value' => $product->getId(), + 'text' => sprintf('%s (%s)', $name, $product->getCode()), + ]; + } + } + + return $this->json($data); + } +} \ No newline at end of file diff --git a/src/Entity/Assignment.php b/src/Entity/Assignment.php index 180aa58..c377834 100644 --- a/src/Entity/Assignment.php +++ b/src/Entity/Assignment.php @@ -25,10 +25,13 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa #[ORM\Column(type: 'string', length: 36, unique: true)] private string $uuid; - #[ORM\Column(length: 64)] - private ?string $busProCode = null; + #[ORM\Column(nullable: true)] + private ?int $destinationBusProId = null; - #[ORM\Column] + #[ORM\ManyToOne] + private ?JobProfile $jobProfile = null; + + #[ORM\Column(nullable: true)] private ?int $available = null; #[ORM\Column(type: Types::DATE_IMMUTABLE)] @@ -37,6 +40,12 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa #[ORM\Column(type: Types::DATE_IMMUTABLE)] private ?\DateTimeImmutable $dateTo = null; + #[ORM\Column(type: Types::DATE_IMMUTABLE)] + private ?\DateTimeImmutable $pickupDate = null; + + #[ORM\Column(nullable: true)] + private ?int $pickup = null; + #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $benefits = null; @@ -77,14 +86,26 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $this->uuid; } - public function getBusProCode(): ?string + public function getDestinationBusProId(): ?int { - return $this->busProCode; + return $this->destinationBusProId; } - public function setBusProCode(string $busProCode): static + public function setDestinationBusProId(?int $destinationBusProId): static { - $this->busProCode = $busProCode; + $this->destinationBusProId = $destinationBusProId; + + return $this; + } + + public function getJobProfile(): ?JobProfile + { + return $this->jobProfile; + } + + public function setJobProfile(?JobProfile $jobProfile): static + { + $this->jobProfile = $jobProfile; return $this; } @@ -94,7 +115,7 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $this->available; } - public function setAvailable(int $available): static + public function setAvailable(?int $available): static { $this->available = $available; @@ -125,6 +146,30 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } + public function getPickupDate(): ?\DateTimeImmutable + { + return $this->pickupDate; + } + + public function setPickupDate(?\DateTimeImmutable $pickupDate): static + { + $this->pickupDate = $pickupDate; + + return $this; + } + + public function getPickup(): ?int + { + return $this->pickup; + } + + public function setPickup(?int $pickup): static + { + $this->pickup = $pickup; + + return $this; + } + public function getBenefits(): ?string { return $this->benefits; diff --git a/src/Form/AssignmentType.php b/src/Form/AssignmentType.php new file mode 100644 index 0000000..28e21b5 --- /dev/null +++ b/src/Form/AssignmentType.php @@ -0,0 +1,70 @@ +add('available', IntegerType::class, [ + 'label' => 'Anzahl', + 'required' => false, + ]) + ->add('destinationBusProId', AutocompleteChoiceType::class, [ + 'label' => 'Destination', + 'endpoint_route' => 'app_admin_autocomplete_hotel', + ]) + ->add('jobProfile', EntityType::class, [ + 'label' => 'Jobprofil', + 'class' => JobProfile::class, + 'choice_label' => 'name', + 'placeholder' => 'Bitte auswählen', + ]) + ->add('dateFrom', DatepickerType::class, [ + 'label' => 'Einsatztermin von', + 'mode' => 'range', + ]) + ->add('dateTo', DatepickerType::class, [ + 'label' => 'Einsatztermin bis', + ]) + ->add('pickupDate', DatepickerType::class, [ + 'label' => 'Busabfahrt', + 'required' => false, + ]) + ->add('pickup', BpnPickupType::class, [ + 'label' => 'Buszustieg', + 'required' => false, + 'placeholder' => 'Keine Busbegleitung', + ]) + ->add('benefits', TextareaType::class, [ + 'label' => 'Benefits', + 'required' => false, + ]) + ->add('fees', EntityType::class, [ + 'label' => 'Honorar(e)', + 'class' => Fee::class, + 'choice_label' => 'name', + 'multiple' => true, + 'expanded' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Assignment::class, + ]); + } +} \ No newline at end of file diff --git a/src/Form/AutocompleteChoiceType.php b/src/Form/AutocompleteChoiceType.php new file mode 100644 index 0000000..a41e5bd --- /dev/null +++ b/src/Form/AutocompleteChoiceType.php @@ -0,0 +1,49 @@ +setDefaults([ + 'endpoint_route' => null, + 'choices' => [], + 'compound' => false, + 'placeholder' => null, + 'endpoint_parameters' => [], + 'controller_action' => null, + ]); + + $resolver->setAllowedTypes('choices', 'array'); + } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + if ($options['endpoint_route']) { + $view->vars['endpoint_url'] = $this->urlGenerator->generate($options['endpoint_route'], $options['endpoint_parameters']); + } else { + $view->vars['choices'] = $options['choices']; + } + $view->vars['placeholder'] = $options['placeholder']; + + $view->vars['initial_label'] = $form->getData(); + $view->vars['initial_value'] = $form->getData(); + $view->vars['action'] = $options['controller_action']; + } + + public function getBlockPrefix(): string + { + return 'autocomplete'; + } +} diff --git a/src/Form/AutocompleteEntityType.php b/src/Form/AutocompleteEntityType.php new file mode 100644 index 0000000..66fae58 --- /dev/null +++ b/src/Form/AutocompleteEntityType.php @@ -0,0 +1,81 @@ +entityManager, $options['class']); + $builder->addModelTransformer($dataTransformer); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setRequired(['class']); + + $resolver->setDefaults([ + 'endpoint_route' => null, + 'choices' => [], + 'compound' => false, + 'label_property' => 'label', + 'label_function' => null, + 'placeholder' => null, + 'endpoint_parameters' => [], + 'controller_action' => null, + ]); + + $resolver->setAllowedTypes('choices', 'array'); + $resolver->setAllowedTypes('label_function', ['null', 'callable']); + } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + if ($options['endpoint_route']) { + $view->vars['endpoint_url'] = $this->urlGenerator->generate($options['endpoint_route'], $options['endpoint_parameters']); + } else { + $view->vars['choices'] = $options['choices']; + } + $view->vars['label_property'] = $options['label_property']; + $view->vars['placeholder'] = $options['placeholder']; + + $view->vars['initial_label'] = ''; + $view->vars['initial_value'] = null; + $view->vars['action'] = $options['controller_action']; + + $entity = $form->getData(); + + if (null !== $entity) { + if (null !== $options['label_function']) { + $labelFunction = $options['label_function']; + $view->vars['initial_label'] = $labelFunction($entity); + } else { + $getter = 'get'.ucfirst($options['label_property']); + if (method_exists($entity, $getter)) { + $view->vars['initial_label'] = $entity->$getter(); + } + } + $view->vars['initial_value'] = $entity->getId(); + } + } + + public function getBlockPrefix(): string + { + return 'autocomplete'; + } +} diff --git a/src/Form/BpnCountryType.php b/src/Form/BpnCountryType.php index 5ac7dfc..42a24b5 100644 --- a/src/Form/BpnCountryType.php +++ b/src/Form/BpnCountryType.php @@ -2,7 +2,7 @@ namespace App\Form; -use App\BusProNet\DataProvider\Countries; +use App\BusProNet\DataProvider\CountryDataProvider; use App\Form\ChoiceLoader\BpnCountryChoiceLoader; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\ChoiceList\ChoiceList; @@ -12,7 +12,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class BpnCountryType extends AbstractType { - public function __construct(private readonly Countries $countries) + public function __construct(private readonly CountryDataProvider $countries) {} public function getParent(): string diff --git a/src/Form/BpnPickupType.php b/src/Form/BpnPickupType.php index 4189bc5..c23ac00 100644 --- a/src/Form/BpnPickupType.php +++ b/src/Form/BpnPickupType.php @@ -2,7 +2,7 @@ namespace App\Form; -use App\BusProNet\DataProvider\Pickups; +use App\BusProNet\DataProvider\PickupDataProvider; use App\Form\ChoiceLoader\BpnPickupsChoiceLoader; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -10,7 +10,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class BpnPickupType extends AbstractType { - public function __construct(private readonly Pickups $pickups) + public function __construct(private readonly PickupDataProvider $pickups) {} public function getParent(): string diff --git a/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php b/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php index 460e45e..ffff4ac 100644 --- a/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php +++ b/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php @@ -2,7 +2,7 @@ namespace App\Form\ChoiceLoader; -use App\BusProNet\DataProvider\Countries; +use App\BusProNet\DataProvider\CountryDataProvider; use App\BusProNet\Model\Country; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; @@ -10,7 +10,7 @@ use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; class BpnCountryChoiceLoader implements ChoiceLoaderInterface { - public function __construct(private readonly Countries $countries, private readonly string $property) + public function __construct(private readonly CountryDataProvider $countries, private readonly string $property) {} public function loadChoiceList(callable $value = null): ChoiceListInterface diff --git a/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php b/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php index 6a9860c..e3b0ab4 100644 --- a/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php +++ b/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php @@ -2,7 +2,7 @@ namespace App\Form\ChoiceLoader; -use App\BusProNet\DataProvider\Pickups; +use App\BusProNet\DataProvider\PickupDataProvider; use App\BusProNet\Model\Pickup; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; @@ -10,7 +10,7 @@ use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; class BpnPickupsChoiceLoader implements ChoiceLoaderInterface { - public function __construct(private readonly Pickups $pickups) + public function __construct(private readonly PickupDataProvider $pickups) {} public function loadChoiceList(callable $value = null): ChoiceListInterface diff --git a/src/Form/ChoiceLoader/BpnProductsChoiceLoader.php b/src/Form/ChoiceLoader/BpnProductsChoiceLoader.php new file mode 100644 index 0000000..a929dfe --- /dev/null +++ b/src/Form/ChoiceLoader/BpnProductsChoiceLoader.php @@ -0,0 +1,41 @@ +products->getAll(); + + foreach ($products as $product) { + $choices[$product->getName()] = $product->getId(); + } + + ksort($choices); + + return new ArrayChoiceList($choices); + + } + + public function loadChoicesForValues(array $values, callable $value = null): array + { + return $values; + } + + public function loadValuesForChoices(array $choices, callable $value = null): array + { + return $choices; + } +} \ No newline at end of file diff --git a/src/Form/DatepickerType.php b/src/Form/DatepickerType.php index c79a583..823017d 100644 --- a/src/Form/DatepickerType.php +++ b/src/Form/DatepickerType.php @@ -19,10 +19,13 @@ class DatepickerType extends AbstractType 'min_date' => null, 'max_date' => null, 'disable_weekends' => false, + 'mode' => 'single', // 'single', 'multiple' or 'range' ]); $resolver->setAllowedTypes('min_date', ['null', \DateTimeImmutable::class]); $resolver->setAllowedTypes('max_date', ['null', \DateTimeImmutable::class]); $resolver->setAllowedTypes('disable_weekends', 'bool'); + $resolver->setAllowedTypes('mode', 'string'); + $resolver->setAllowedValues('mode', ['single', 'multiple', 'range']); } public function buildView(FormView $view, FormInterface $form, array $options): void @@ -30,6 +33,7 @@ class DatepickerType extends AbstractType $view->vars['min_date'] = $options['min_date']; $view->vars['max_date'] = $options['max_date']; $view->vars['disable_weekends'] = $options['disable_weekends']; + $view->vars['mode'] = $options['mode']; } public function getParent(): string diff --git a/src/Menu/AdminMenuBuilder.php b/src/Menu/AdminMenuBuilder.php index 023493f..eb0c15f 100644 --- a/src/Menu/AdminMenuBuilder.php +++ b/src/Menu/AdminMenuBuilder.php @@ -143,7 +143,9 @@ class AdminMenuBuilder extends AbstractMenuBuilder $menu->addChild('zurück zur Übersicht', [ 'route' => 'app_admin_teamer_index', - 'icon' => 'back', + 'extras' => [ + 'icon' => 'back', + ], ]); return $menu; diff --git a/src/Twig/AppRuntime.php b/src/Twig/AppRuntime.php index 35830b1..97bd1ab 100644 --- a/src/Twig/AppRuntime.php +++ b/src/Twig/AppRuntime.php @@ -2,8 +2,8 @@ namespace App\Twig; -use App\BusProNet\DataProvider\Countries; -use App\BusProNet\DataProvider\Pickups; +use App\BusProNet\DataProvider\CountryDataProvider; +use App\BusProNet\DataProvider\PickupDataProvider; use App\BusProNet\Model\Country; use App\Entity\Teamer; use Carbon\Carbon; @@ -15,11 +15,11 @@ use Twig\Extra\Intl\IntlExtension; class AppRuntime implements RuntimeExtensionInterface { public function __construct( - private readonly RequestStack $requestStack, - private readonly IntlExtension $intlExtension, - private readonly Countries $countries, - private readonly Pickups $pickups, - private readonly string $environment + private readonly RequestStack $requestStack, + private readonly IntlExtension $intlExtension, + private readonly CountryDataProvider $countries, + private readonly PickupDataProvider $pickups, + private readonly string $environment ) { } diff --git a/templates/_partials/_menu.html.twig b/templates/_partials/_menu.html.twig index 2c2256d..a78107e 100644 --- a/templates/_partials/_menu.html.twig +++ b/templates/_partials/_menu.html.twig @@ -64,7 +64,7 @@ {% elseif item.extras.divider == true %}