diff --git a/assets/controllers/upload_collection_controller.js b/assets/controllers/upload_collection_controller.js index 89cc23e..ddc0811 100644 --- a/assets/controllers/upload_collection_controller.js +++ b/assets/controllers/upload_collection_controller.js @@ -1,15 +1,12 @@ import { Controller } from '@hotwired/stimulus' import { useFetch } from '../mixins/use_fetch' import Dropzone from 'dropzone' -import 'dropzone/dist/dropzone.css' Dropzone.autoDiscover = false /* stimulusFetch: 'lazy' */ export default class extends Controller { - static targets = [ - 'dropzone', - ] + static targets = ['dropzone', 'errors', 'previewTemplate'] static values = { endpointUpload: String, @@ -19,6 +16,7 @@ export default class extends Controller { acceptedFiles: String, chunking: Boolean, params: Object, + language: Object, } connect () { @@ -30,14 +28,26 @@ export default class extends Controller { maxFiles: this.maxFilesValue, maxFilesize: this.maxFilesizeValue, acceptedFiles: this.acceptedFilesValue, - chunking: this.chunkingValue, + chunking: true, chunkSize: 10000000, parallelUploads: 3, retryChunks: true, disablePreviews: false, + previewTemplate: this.previewTemplateTarget.innerHTML, createImageThumbnails: true, autoProcessQueue: true, - addRemoveLinks: true, + addRemoveLinks: false, + dictFileTooBig: this.languageValue.fileTooBig, + dictInvalidFileType: this.languageValue.invalidFileType, + dictMaxFilesExceeded: this.languageValue.maxFilesExceeded, + }) + + dropzone.on('addedfile', file => { + this.errorsTarget.classList.add('hidden') + }) + + dropzone.on('removedfile', (file) => { + fetch(`${this.endpointDeleteValue}?uuid=${file.upload.uuid}`, { method: 'delete', credentials: 'include' }) }) dropzone.on('sending', (file, xhr, formData) => { @@ -56,8 +66,10 @@ export default class extends Controller { window.dispatchEvent(new CustomEvent('dropzone:complete')) }) - dropzone.on('removedfile', (file) => { - fetch(`${this.endpointDeleteValue}?uuid=${file.upload.uuid}`, { method: 'delete', credentials: 'include' }) + dropzone.on('error', (file, errorMessage) => { + this.errorsTarget.innerText = errorMessage + this.errorsTarget.classList.remove('hidden') + dropzone.removeFile(file) }) } } diff --git a/migrations/Version20230930150355.php b/migrations/Version20230930150355.php new file mode 100644 index 0000000..10a0ccb --- /dev/null +++ b/migrations/Version20230930150355.php @@ -0,0 +1,49 @@ +addSql('ALTER TABLE disposition_requirement DROP FOREIGN KEY FK_F3C6374CC26E160B'); + $this->addSql('ALTER TABLE assignment_pickup DROP FOREIGN KEY FK_79BF19E0C26E160B'); + $this->addSql('ALTER TABLE assignment_pickup DROP FOREIGN KEY FK_79BF19E0D19302F8'); + $this->addSql('DROP TABLE pickup'); + $this->addSql('DROP TABLE assignment_pickup'); + $this->addSql('DROP INDEX IDX_F3C6374CC26E160B ON disposition_requirement'); + $this->addSql('ALTER TABLE disposition_requirement DROP pickup_id'); + $this->addSql('ALTER TABLE job_profile CHANGE required_licenses required_licenses JSON NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE teamer ADD pickups JSON NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE user CHANGE roles roles JSON NOT NULL COMMENT \'(DC2Type:json)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE pickup (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, bus_pro_id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('CREATE TABLE assignment_pickup (assignment_id INT NOT NULL, pickup_id INT NOT NULL, INDEX IDX_79BF19E0D19302F8 (assignment_id), INDEX IDX_79BF19E0C26E160B (pickup_id), PRIMARY KEY(assignment_id, pickup_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE assignment_pickup ADD CONSTRAINT FK_79BF19E0C26E160B FOREIGN KEY (pickup_id) REFERENCES pickup (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE assignment_pickup ADD CONSTRAINT FK_79BF19E0D19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE user CHANGE roles roles JSON NOT NULL COMMENT \'(DC2Type:json)\''); + $this->addSql('ALTER TABLE teamer DROP pickups'); + $this->addSql('ALTER TABLE disposition_requirement ADD pickup_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE disposition_requirement ADD CONSTRAINT FK_F3C6374CC26E160B FOREIGN KEY (pickup_id) REFERENCES pickup (id)'); + $this->addSql('CREATE INDEX IDX_F3C6374CC26E160B ON disposition_requirement (pickup_id)'); + $this->addSql('ALTER TABLE job_profile CHANGE required_licenses required_licenses JSON NOT NULL COMMENT \'(DC2Type:json)\''); + } +} diff --git a/migrations/Version20230930150821.php b/migrations/Version20230930150821.php new file mode 100644 index 0000000..0eeebb6 --- /dev/null +++ b/migrations/Version20230930150821.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE teamer ADD language VARCHAR(255) NOT NULL, ADD size VARCHAR(2) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE teamer DROP language, DROP size'); + } +} diff --git a/package-lock.json b/package-lock.json index 32ab7ea..53f755f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "html", + "name": "myep-team", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php index 9949c0b..27e5f02 100644 --- a/src/BusProNet/ApiClient.php +++ b/src/BusProNet/ApiClient.php @@ -2,24 +2,29 @@ namespace App\BusProNet; -use App\BusProNet\Model\BaseResponse; +use App\BusProNet\Model\BaseDataResponse; +use App\BusProNet\Model\CrmAttributesResponse; +use App\BusProNet\Model\NotificationResponse; +use App\BusProNet\Model\ProfileResponse; use App\Entity\User; use Psr\Log\LoggerInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Serializer\SerializerInterface; -use Symfony\Contracts\Cache\CacheInterface; -use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; class ApiClient { + public const TYPE_NOTIFICATION = 'HINWEIS'; + public const TYPE_CUSTOMER_DATA = 'KUNDENKONTO'; + public const TYPE_BASE_DATA_COUNTRIES = 'STAMMLAENDER'; + public const TYPE_BASE_DATA_PICKUPS = 'STAMMZUSTIEGE'; + private array $config; public function __construct( private readonly HttpClientInterface $httpClient, private readonly SerializerInterface $serializer, private readonly ResponseParser $responseParser, - private readonly CacheInterface $cache, private readonly LoggerInterface $logger, array $options ) { @@ -29,41 +34,26 @@ class ApiClient /** * @throws ApiClientException */ - public function getProfile(string $email, string $password): BaseResponse + public function getProfile(string $email, string $password): ProfileResponse { $data = [ 'anfrage' => [ 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'KUNDENKONTO'), - 'satz' => ['@typ' => 'KUNDENKONTO'], + 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA), + 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA], 'art' => 'Adressdaten', 'email' => $email, 'passwort' => md5($password), ], ]; - $body = $this - ->serializer - ->serialize($data, 'xml'); - - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ] - ]); - - $xml = $response->getContent(); - - return $this->responseParser->parseXmlString($xml); - } catch (\Throwable $e) { - } - - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); + return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data); } - public function updateProfile(User $user, string $password): BaseResponse + /** + * @throws ApiClientException + */ + public function updateProfile(User $user, string $password): ProfileResponse { if (null === $teamer = $user->getTeamer()) { throw new ApiClientException('Invalid argument'); @@ -72,8 +62,8 @@ class ApiClient $data = [ 'anfrage' => [ 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'KUNDENKONTO'), - 'satz' => ['@typ' => 'KUNDENKONTO'], + 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA), + 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA], 'art' => 'Adressdaten_Ändern', 'email' => $user->getEmail(), 'passwort' => md5($password), @@ -82,79 +72,71 @@ class ApiClient ], ]; - $body = $this - ->serializer - ->serialize($data, 'xml'); - - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ] - ]); - - $xml = $response->getContent(); - - return $this->responseParser->parseXmlString($xml); - } catch (\Throwable $e) { - } - - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); - } - - public function resetPassword(string $email): BaseResponse - { - $data = [ - 'anfrage' => [ - 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'KUNDENKONTO'), - 'satz' => ['@typ' => 'KUNDENKONTO'], - 'art' => 'Passwort_Anfrage', - 'email' => $email, - ], - ]; - - $body = $this - ->serializer - ->serialize($data, 'xml'); - - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ] - ]); - - $xml = $response->getContent(); - - return $this->responseParser->parseXmlString($xml); - } catch (\Throwable $e) { - } - - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); + return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data); } /** * @throws ApiClientException */ - public function getCrmAttributes(string $email, string $password): BaseResponse + public function resetPassword(string $email): NotificationResponse { $data = [ 'anfrage' => [ 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'KUNDENKONTO'), - 'satz' => ['@typ' => 'KUNDENKONTO'], + 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA), + 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA], + 'art' => 'Passwort_Anfrage', + 'email' => $email, + ], + ]; + + return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data); + } + + /** + * @throws ApiClientException + */ + public function getCrmAttributes(string $email, string $password): CrmAttributesResponse + { + $data = [ + 'anfrage' => [ + 'user' => $this->config['bpn_username'], + 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA), + 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA], 'art' => 'SelektionCRM', 'email' => $email, 'passwort' => md5($password), ], ]; + return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data); + } + + /** + * @throws ApiClientException + */ + public function getBaseData(string $type): BaseDataResponse + { + $data = [ + 'anfrage' => [ + 'user' => $this->config['bpn_username'], + 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], $type), + 'satz' => ['@typ' => $type], + ], + ]; + + return $this->sendRequest($type, $data); + } + + /** + * @throws ApiClientException + */ + private function sendRequest(string $type, array $data): mixed + { $body = $this ->serializer - ->serialize($data, 'xml'); + ->serialize($data, 'xml') + ; try { $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ @@ -165,7 +147,7 @@ class ApiClient $xml = $response->getContent(); - return $this->responseParser->parseXmlString($xml); + return $this->responseParser->parseXmlString($type, $xml); } catch (\Throwable $e) { } @@ -173,76 +155,6 @@ class ApiClient throw new ApiClientException($e->getMessage()); } - public function getCountries(): BaseResponse - { - return $this->cache->get('bpn_countries', function (ItemInterface $item) { - $item->expiresAfter(3600); - - $data = [ - 'anfrage' => [ - 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'STAMMLAENDER'), - 'satz' => ['@typ' => 'STAMMLAENDER'], - ], - ]; - - $body = $this - ->serializer - ->serialize($data, 'xml'); - - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ] - ]); - - $xml = $response->getContent(); - - return $this->responseParser->parseXmlString($xml); - } catch (\Throwable $e) { - } - - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); - }); - } - - public function getPickups(): BaseResponse - { - return $this->cache->get('bpn_pickups', function (ItemInterface $item) { - $item->expiresAfter(3600); - - $data = [ - 'anfrage' => [ - 'user' => $this->config['bpn_username'], - 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], 'STAMMZUSTIEGE'), - 'satz' => ['@typ' => 'STAMMZUSTIEGE'], - ], - ]; - - $body = $this - ->serializer - ->serialize($data, 'xml'); - - try { - $response = $this->httpClient->request('GET', $this->config['bpn_url'], [ - 'query' => [ - 'operation' => $body, - ] - ]); - - $xml = $response->getContent(); - - return $this->responseParser->parseXmlString($xml); - } catch (\Throwable $e) { - } - - $this->logger->error('API error', ['error' => $e->getMessage()]); - throw new ApiClientException($e->getMessage()); - }); - } - private function createKey(string $username, string $password, string $type): string { $date = (new \DateTimeImmutable())->format('Ymd'); diff --git a/src/BusProNet/DataProvider/Countries.php b/src/BusProNet/DataProvider/Countries.php new file mode 100644 index 0000000..9c00fa2 --- /dev/null +++ b/src/BusProNet/DataProvider/Countries.php @@ -0,0 +1,33 @@ +cache->get('bpn_countries', function (ItemInterface $item) { + $item->expiresAfter(3600); + + return $this + ->apiClient + ->getBaseData(ApiClient::TYPE_BASE_DATA_COUNTRIES) + ->getItems() + ; + }); + } catch (ApiClientException $e) { + $countries = []; + } + + return $countries; + } +} \ No newline at end of file diff --git a/src/BusProNet/DataProvider/Pickups.php b/src/BusProNet/DataProvider/Pickups.php new file mode 100644 index 0000000..0307c70 --- /dev/null +++ b/src/BusProNet/DataProvider/Pickups.php @@ -0,0 +1,34 @@ +cache->get('bpn_pickups', function (ItemInterface $item) { + $item->expiresAfter(3600); + + return $this + ->apiClient + ->getBaseData(ApiClient::TYPE_BASE_DATA_PICKUPS) + ->getItems() + ; + }); + } catch (ApiClientException $e) { + $pickups = []; + } + + return $pickups; + + } +} \ No newline at end of file diff --git a/src/BusProNet/Model/BaseDataResponse.php b/src/BusProNet/Model/BaseDataResponse.php new file mode 100644 index 0000000..d2c0d65 --- /dev/null +++ b/src/BusProNet/Model/BaseDataResponse.php @@ -0,0 +1,20 @@ +items; + } + + public function setItems(array $items): static + { + $this->items = $items; + + return $this; + } +} \ No newline at end of file diff --git a/src/BusProNet/Model/CountriesResponse.php b/src/BusProNet/Model/CountriesResponse.php deleted file mode 100644 index 1e1f94c..0000000 --- a/src/BusProNet/Model/CountriesResponse.php +++ /dev/null @@ -1,20 +0,0 @@ -countries; - } - - public function setCountries(array $countries): static - { - $this->countries = $countries; - - return $this; - } -} \ No newline at end of file diff --git a/src/BusProNet/Model/CrmAttributesResponse.php b/src/BusProNet/Model/CrmAttributesResponse.php index bec74c1..c524836 100644 --- a/src/BusProNet/Model/CrmAttributesResponse.php +++ b/src/BusProNet/Model/CrmAttributesResponse.php @@ -2,7 +2,7 @@ namespace App\BusProNet\Model; -class CrmAttributesResponse extends BaseResponse +class CrmAttributesResponse { private ?array $attributeGroups = null; private bool $admin = false; diff --git a/src/BusProNet/Model/BaseResponse.php b/src/BusProNet/Model/NotificationResponse.php similarity index 97% rename from src/BusProNet/Model/BaseResponse.php rename to src/BusProNet/Model/NotificationResponse.php index 11fde4f..be7c06a 100644 --- a/src/BusProNet/Model/BaseResponse.php +++ b/src/BusProNet/Model/NotificationResponse.php @@ -2,7 +2,7 @@ namespace App\BusProNet\Model; -class BaseResponse +class NotificationResponse { private ?int $code; private ?string $message; diff --git a/src/BusProNet/Model/PickupsResponse.php b/src/BusProNet/Model/PickupsResponse.php deleted file mode 100644 index c60e07d..0000000 --- a/src/BusProNet/Model/PickupsResponse.php +++ /dev/null @@ -1,20 +0,0 @@ -pickups; - } - - public function setPickups(array $pickups): static - { - $this->pickups = $pickups; - - return $this; - } -} \ No newline at end of file diff --git a/src/BusProNet/Model/ProfileResponse.php b/src/BusProNet/Model/ProfileResponse.php index 93ab3f0..183f14e 100644 --- a/src/BusProNet/Model/ProfileResponse.php +++ b/src/BusProNet/Model/ProfileResponse.php @@ -2,7 +2,7 @@ namespace App\BusProNet\Model; -class ProfileResponse extends BaseResponse +class ProfileResponse { private ?int $addressId = null; private ?int $personId = null; diff --git a/src/BusProNet/ResponseParser.php b/src/BusProNet/ResponseParser.php index 366fac4..dc376d8 100644 --- a/src/BusProNet/ResponseParser.php +++ b/src/BusProNet/ResponseParser.php @@ -3,16 +3,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\CountriesResponse; use App\BusProNet\Model\Country; use App\BusProNet\Model\CrmAttribute; -use App\BusProNet\Model\CrmAttributesResponse; use App\BusProNet\Model\CrmAttributeGroup; +use App\BusProNet\Model\CrmAttributesResponse; use App\BusProNet\Model\Pickup; -use App\BusProNet\Model\PickupsResponse; use App\BusProNet\Model\ProfileResponse; -use App\BusProNet\Model\BaseResponse; use Symfony\Component\OptionsResolver\OptionsResolver; class ResponseParser @@ -27,37 +26,46 @@ class ResponseParser /** * @throws ResponseParserException */ - public function parseXmlString(string $content): BaseResponse + public function parseXmlString(string $type, string $content): mixed { $xml = simplexml_load_string($content); - $type = (string)$xml->xpath('satz/@typ')[0]; - switch ($type) { - case 'HINWEIS': - return $this->createBaseResponse($xml); - case 'KUNDENKONTO': + // Override type when present in XML to catch error responses + $responseType = $type; + $responseTypeXml = $xml->xpath('satz/@typ'); + if (0 < count($responseTypeXml)) { + $responseType = (string)$xml->xpath('satz/@typ')[0]; + } + + switch ($responseType) { + case ApiClient::TYPE_NOTIFICATION: + return $this->createNotificationResponse($xml); + case ApiClient::TYPE_CUSTOMER_DATA: $subType = (string) $xml->xpath('art')[0]; switch ($subType) { case 'Adressdaten': + case 'Adressdaten_Ändern': return $this->createProfileResponse($xml); case 'SelektionCRM': + case 'SelektionCRM_Ändern': return $this->createCrmAttributesResponse($xml); } - case 'STAMMLAENDER': + break; + case ApiClient::TYPE_BASE_DATA_COUNTRIES: return $this->createCountriesResponse($xml); - case 'STAMMZUSTIEGE': + case ApiClient::TYPE_BASE_DATA_PICKUPS: return $this->createPickupsResponse($xml); } throw new ResponseParserException('Unable to parse XML response'); } - public function createBaseResponse(\SimpleXMLElement $xml): BaseResponse + public function createNotificationResponse(\SimpleXMLElement $xml): NotificationResponse { $code = (int) $xml->xpath('satz/nr')[0]; $message = (string) $xml->xpath('satz/text')[0]; - return new BaseResponse($code, $message); + return new NotificationResponse($code, $message); } public function createProfileResponse(\SimpleXMLElement $xml): ProfileResponse @@ -163,7 +171,7 @@ class ResponseParser return $response; } - public function createCountriesResponse(\SimpleXMLElement $xml): CountriesResponse + public function createCountriesResponse(\SimpleXMLElement $xml): BaseDataResponse { $countries = []; @@ -178,17 +186,17 @@ class ResponseParser $countries[] = $country; } - $response = new CountriesResponse(); - $response->setCountries($countries); + $response = new BaseDataResponse(); + $response->setItems($countries); return $response; } - public function createPickupsResponse(\SimpleXMLElement $xml): PickupsResponse + public function createPickupsResponse(\SimpleXMLElement $xml): BaseDataResponse { $pickups = []; - foreach ($xml->xpath('zustiege/zustieg') as $item) { + foreach ($xml->xpath('zustieg') as $item) { $pickup = new Pickup(); $pickup ->setId((int)$item->attributes()['id']) @@ -200,8 +208,8 @@ class ResponseParser $pickups[] = $pickup; } - $response = new PickupsResponse(); - $response->setPickups($pickups); + $response = new BaseDataResponse(); + $response->setItems($pickups); return $response; } diff --git a/src/Entity/Assignment.php b/src/Entity/Assignment.php index b5b77a2..180aa58 100644 --- a/src/Entity/Assignment.php +++ b/src/Entity/Assignment.php @@ -46,9 +46,6 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa #[ORM\ManyToOne] private ?User $owner = null; - #[ORM\ManyToMany(targetEntity: Pickup::class)] - private Collection $pickups; - #[ORM\ManyToMany(targetEntity: Fee::class)] private Collection $fees; @@ -64,7 +61,6 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa public function __construct() { $this->uuid = Uuid::v4(); - $this->pickups = new ArrayCollection(); $this->fees = new ArrayCollection(); $this->applications = new ArrayCollection(); $this->dispositions = new ArrayCollection(); @@ -165,30 +161,6 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } - /** - * @return Collection - */ - public function getPickups(): Collection - { - return $this->pickups; - } - - public function addPickup(Pickup $pickup): static - { - if (!$this->pickups->contains($pickup)) { - $this->pickups->add($pickup); - } - - return $this; - } - - public function removePickup(Pickup $pickup): static - { - $this->pickups->removeElement($pickup); - - return $this; - } - /** * @return Collection */ diff --git a/src/Entity/DispositionRequirement.php b/src/Entity/DispositionRequirement.php index 1164200..2fe81dc 100644 --- a/src/Entity/DispositionRequirement.php +++ b/src/Entity/DispositionRequirement.php @@ -17,9 +17,6 @@ class DispositionRequirement #[ORM\ManyToOne] private ?Training $training = null; - #[ORM\ManyToOne] - private ?Pickup $pickup = null; - #[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $pickupDate = null; @@ -43,18 +40,6 @@ class DispositionRequirement return $this; } - public function getPickup(): ?Pickup - { - return $this->pickup; - } - - public function setPickup(?Pickup $pickup): static - { - $this->pickup = $pickup; - - return $this; - } - public function getPickupDate(): ?\DateTimeImmutable { return $this->pickupDate; diff --git a/src/Entity/Pickup.php b/src/Entity/Pickup.php deleted file mode 100644 index 6eb9a06..0000000 --- a/src/Entity/Pickup.php +++ /dev/null @@ -1,50 +0,0 @@ -id; - } - - public function getName(): ?string - { - return $this->name; - } - - public function setName(string $name): static - { - $this->name = $name; - - return $this; - } - - public function getBusProId(): ?int - { - return $this->busProId; - } - - public function setBusProId(int $busProId): static - { - $this->busProId = $busProId; - - return $this; - } -} diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php index 7d9b269..8ff87df 100644 --- a/src/Entity/Teamer.php +++ b/src/Entity/Teamer.php @@ -114,6 +114,16 @@ class Teamer implements TimestampableEntityInterface #[ORM\OneToOne(mappedBy: 'teamer')] private ?User $user = null; + #[ORM\Column] + private array $pickups = []; + + #[ORM\Column(length: 255)] + #[Assert\NotBlank(message: 'Bitte gib deine Sprache(n) an', groups: ['profile', 'profile_preflight'])] + private ?string $language = null; + + #[ORM\Column(length: 2)] + private ?string $size = null; + public function __construct() { $this->uuid = Uuid::v4(); @@ -615,4 +625,40 @@ class Teamer implements TimestampableEntityInterface return $this; } + + public function getPickups(): array + { + return $this->pickups; + } + + public function setPickups(array $pickups): static + { + $this->pickups = $pickups; + + return $this; + } + + public function getLanguage(): ?string + { + return $this->language; + } + + public function setLanguage(?string $language): static + { + $this->language = $language; + + return $this; + } + + public function getSize(): ?string + { + return $this->size; + } + + public function setSize(string $size): static + { + $this->size = $size; + + return $this; + } } diff --git a/src/Form/BpnCountryType.php b/src/Form/BpnCountryType.php index 56545ed..5ac7dfc 100644 --- a/src/Form/BpnCountryType.php +++ b/src/Form/BpnCountryType.php @@ -2,7 +2,7 @@ namespace App\Form; -use App\BusProNet\ApiClient; +use App\BusProNet\DataProvider\Countries; 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 ApiClient $apiClient) + public function __construct(private readonly Countries $countries) {} public function getParent(): string @@ -29,7 +29,7 @@ class BpnCountryType extends AbstractType 'choice_loader' => function (Options $options) { return ChoiceList::loader( $this, - new BpnCountryChoiceLoader($this->apiClient, $options['property']), + new BpnCountryChoiceLoader($this->countries, $options['property']), [$options['property']] ); }, diff --git a/src/Form/BpnPickupType.php b/src/Form/BpnPickupType.php new file mode 100644 index 0000000..7a455bb --- /dev/null +++ b/src/Form/BpnPickupType.php @@ -0,0 +1,27 @@ +setDefaults([ + 'choice_loader' => new BpnPickupsChoiceLoader($this->pickups), + ]); + } +} \ No newline at end of file diff --git a/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php b/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php index 9deb2db..460e45e 100644 --- a/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php +++ b/src/Form/ChoiceLoader/BpnCountryChoiceLoader.php @@ -2,29 +2,22 @@ namespace App\Form\ChoiceLoader; -use App\BusProNet\ApiClient; -use App\BusProNet\ApiClientException; -use App\BusProNet\Model\CountriesResponse; +use App\BusProNet\DataProvider\Countries; +use App\BusProNet\Model\Country; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; class BpnCountryChoiceLoader implements ChoiceLoaderInterface { - public function __construct(private readonly ApiClient $apiClient, private readonly string $property) + public function __construct(private readonly Countries $countries, private readonly string $property) {} public function loadChoiceList(callable $value = null): ChoiceListInterface { - try { - /** @var CountriesResponse $response */ - $response = $this->apiClient->getCountries(); - $countries = $response->getCountries(); - } catch (ApiClientException $e) { - $countries = []; - } - $choices = []; + /** @var Country[] $countries */ + $countries = $this->countries->getAll(); foreach ($countries as $country) { $key = 'nationality' === $this->property ? $country->getNationality() : $country->getName(); diff --git a/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php b/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php new file mode 100644 index 0000000..6a9860c --- /dev/null +++ b/src/Form/ChoiceLoader/BpnPickupsChoiceLoader.php @@ -0,0 +1,41 @@ +pickups->getAll(); + + foreach ($pickups as $pickup) { + $choices[$pickup->getCity()] = $pickup->getBusProId(); + } + + 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/TeamerProfileType.php b/src/Form/TeamerProfileType.php index f8372c2..94f876c 100644 --- a/src/Form/TeamerProfileType.php +++ b/src/Form/TeamerProfileType.php @@ -8,6 +8,7 @@ use App\Service\Upload\UploadHandler; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\BirthdayType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -79,6 +80,32 @@ class TeamerProfileType extends AbstractType 'label' => false, 'error_bubbling' => false, ]) + ->add('pickups', CollectionType::class, [ + 'label' => 'Zustiege', + 'entry_type' => BpnPickupType::class, + 'allow_add' => true, + 'allow_delete' => true, + ]) + ->add('language', TextType::class, [ + 'label' => 'Sprache(n)', + ]) + ->add('size', ChoiceType::class, [ + 'label' => 'Klamottengröße', + 'choices' => [ + 'S' => 'S', + 'M' => 'M', + 'L' => 'L', + 'XL' => 'XL', + 'XXL' => 'XXL', + ], + ]) + ->add('status', ChoiceType::class, [ + 'label' => 'Status', + 'choices' => [ + 'Neuteamer' => Teamer::STATUS_NEW, + 'Bestandsteamer' => Teamer::STATUS_EXISTING, + ], + ]) ; } diff --git a/templates/_partials/_upload_collection_form.html.twig b/templates/_partials/_upload_collection_form.html.twig index 96f987b..68740d1 100644 --- a/templates/_partials/_upload_collection_form.html.twig +++ b/templates/_partials/_upload_collection_form.html.twig @@ -1,16 +1,38 @@
-
+
+
+ {{ icon('upload', 'w-6 h-6') }} + {{ 'uploader.drop_files'|trans }} {{ 'uploader.click_here'|trans }} +
+ + +
diff --git a/templates/layout.html.twig b/templates/layout.html.twig index 9f2bb27..b9c11c4 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -6,15 +6,24 @@ My E&P Team - - +
+ {% if app.user.teamer.photo %} + + {{ teamer.firstName }} + + {% endif %} + + +