diff --git a/migrations/Version20260817090000.php b/migrations/Version20260817090000.php new file mode 100644 index 0000000..54b5808 --- /dev/null +++ b/migrations/Version20260817090000.php @@ -0,0 +1,27 @@ +addSql('ALTER TABLE user ADD first_name VARCHAR(100) DEFAULT NULL, ADD last_name VARCHAR(100) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE user DROP first_name, DROP last_name'); + } +} diff --git a/src/Controller/Account/PersonalDataController.php b/src/Controller/Account/PersonalDataController.php index fb65892..26b1eaa 100644 --- a/src/Controller/Account/PersonalDataController.php +++ b/src/Controller/Account/PersonalDataController.php @@ -129,7 +129,13 @@ class PersonalDataController extends AbstractController // Update profile completeness flag on user entity $isComplete = $this->completenessChecker->isComplete($personalData); - $user->setProfileComplete($isComplete); + $user + ->setProfileComplete($isComplete) + // Otherwise a rename here would stay invisible in the backend until the next + // login, which is the only other place the name is synced from BPN. + ->setFirstName($personalData->firstName) + ->setLastName($personalData->name) + ; $this->entityManager->flush(); $this->addFlash('success', 'Deine persönlichen Daten wurden aktualisiert'); diff --git a/src/Entity/User.php b/src/Entity/User.php index 425452d..794e4b0 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -27,6 +27,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column(type: 'integer', nullable: true)] private ?int $addressId = null; + #[ORM\Column(type: 'string', length: 100, nullable: true)] + private ?string $firstName = null; + + #[ORM\Column(type: 'string', length: 100, nullable: true)] + private ?string $lastName = null; + /** * @var array */ @@ -91,6 +97,46 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + public function getFirstName(): ?string + { + return $this->firstName; + } + + public function setFirstName(?string $firstName): static + { + $this->firstName = $firstName; + + return $this; + } + + public function getLastName(): ?string + { + return $this->lastName; + } + + public function setLastName(?string $lastName): static + { + $this->lastName = $lastName; + + return $this; + } + + public function getFullName(): ?string + { + $fullName = trim(sprintf('%s %s', $this->firstName, $this->lastName)); + + return '' !== $fullName ? $fullName : null; + } + + /** + * The name to identify this account by in the UI. BusPro only fills the name in on login, so + * accounts that have not signed in since the columns were added fall back to their e-mail. + */ + public function getDisplayName(): string + { + return $this->getFullName() ?? (string) $this->email; + } + public function getPassword(): ?string { return base64_decode($this->password); diff --git a/src/Form/Admin/Filter/AccommodationBookingFilterOptionsProvider.php b/src/Form/Admin/Filter/AccommodationBookingFilterOptionsProvider.php index e3545ef..7953abc 100644 --- a/src/Form/Admin/Filter/AccommodationBookingFilterOptionsProvider.php +++ b/src/Form/Admin/Filter/AccommodationBookingFilterOptionsProvider.php @@ -57,7 +57,7 @@ final readonly class AccommodationBookingFilterOptionsProvider $managers[(int) $manager->getId()] = $manager; } - uasort($managers, static fn (User $a, User $b) => strcasecmp((string) $a->getEmail(), (string) $b->getEmail())); + uasort($managers, static fn (User $a, User $b) => strcasecmp($a->getDisplayName(), $b->getDisplayName())); return array_values($managers); } diff --git a/src/Form/Admin/Filter/AccommodationBookingFilterType.php b/src/Form/Admin/Filter/AccommodationBookingFilterType.php index 84749e0..d86f338 100644 --- a/src/Form/Admin/Filter/AccommodationBookingFilterType.php +++ b/src/Form/Admin/Filter/AccommodationBookingFilterType.php @@ -63,7 +63,7 @@ class AccommodationBookingFilterType extends AbstractListFilterType 'label' => 'Betreuer:in', 'class' => User::class, 'choices' => $options['managers'], - 'choice_label' => 'email', + 'choice_label' => 'displayName', 'placeholder' => 'alle', 'required' => false, ]]; diff --git a/src/Form/Admin/Groups/AccommodationBookingType.php b/src/Form/Admin/Groups/AccommodationBookingType.php index 6b6689a..b89d897 100644 --- a/src/Form/Admin/Groups/AccommodationBookingType.php +++ b/src/Form/Admin/Groups/AccommodationBookingType.php @@ -130,7 +130,7 @@ class AccommodationBookingType extends AbstractType 'class' => User::class, 'required' => false, 'choices' => $options['assignable_managers'], - 'choice_label' => 'email', + 'choice_label' => 'displayName', 'placeholder' => 'keine Zuordnung', 'label' => 'Bearbeiter:in', ]); diff --git a/src/Form/Model/Filter/AccommodationBookingFilterDto.php b/src/Form/Model/Filter/AccommodationBookingFilterDto.php index aa321cd..ec12fd5 100644 --- a/src/Form/Model/Filter/AccommodationBookingFilterDto.php +++ b/src/Form/Model/Filter/AccommodationBookingFilterDto.php @@ -81,7 +81,7 @@ class AccommodationBookingFilterDto extends AbstractListFilterDto } elseif (null !== $this->managedBy) { $chips[] = new ListFilterChip( 'Betreuer:in', - (string) $this->managedBy->getEmail(), + $this->managedBy->getDisplayName(), $this->managedByLocked ? [] : ['managedBy'], ); } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 6deb06c..818f948 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -29,6 +29,8 @@ class UserRepository extends ServiceEntityRepository $this->applySearchTerm($qb, $filter->searchTerm(), [ 'user.email', + 'user.firstName', + 'user.lastName', 'user.addressId', 'user.personId', ]); diff --git a/src/Security/BpnAuthenticator.php b/src/Security/BpnAuthenticator.php index a4a5997..8d673bf 100644 --- a/src/Security/BpnAuthenticator.php +++ b/src/Security/BpnAuthenticator.php @@ -119,6 +119,8 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent ->setPassword($encryptedPassword) ->setPersonId($personalData->personId) ->setAddressId($personalData->addressId) + ->setFirstName($personalData->firstName) + ->setLastName($personalData->name) ->setLastLoginAt(new \DateTimeImmutable()) ->setProfileComplete($this->completenessChecker->isComplete($personalData)) ; diff --git a/templates/admin/accommodation_booking/index.html.twig b/templates/admin/accommodation_booking/index.html.twig index 37c1a5e..1f123eb 100644 --- a/templates/admin/accommodation_booking/index.html.twig +++ b/templates/admin/accommodation_booking/index.html.twig @@ -60,7 +60,7 @@ {% if booking.managedBy is not null %} - {{ booking.managedBy.email }} + {{ booking.managedBy.displayName }} {% else %} keine Zuordnung {% endif %} diff --git a/templates/admin/user/index.html.twig b/templates/admin/user/index.html.twig index bbbc361..c20549f 100644 --- a/templates/admin/user/index.html.twig +++ b/templates/admin/user/index.html.twig @@ -13,6 +13,9 @@ + @@ -37,6 +40,9 @@ {% for user in pagination %} + @@ -65,7 +71,7 @@ {% else %} -
+ {{ knp_pagination_sortable(pagination, 'Name', 'user.lastName') }} + {{ knp_pagination_sortable(pagination, 'E-Mail', 'user.email') }}
+ {{ user.fullName | default('-') }} + {{ user.email }}
+ {{ filter.isActive ? 'Keine Treffer für diesen Filter.' : 'Keine Daten...' }}