diff --git a/src/Controller/Admin/Feedback/ProvideController.php b/src/Controller/Admin/Feedback/ProvideController.php index 1616bc7..6523b0b 100644 --- a/src/Controller/Admin/Feedback/ProvideController.php +++ b/src/Controller/Admin/Feedback/ProvideController.php @@ -37,8 +37,8 @@ class ProvideController extends AbstractController // Feedback provided by admins is published instantly $feedback->setStatus(Feedback::STATUS_PUBLISHED); - if (null !== $form->get('authorOverride')->getData()) { - $feedback->setAuthor($form->get('authorOverride')->getData()); + if (null !== $author = $form->get('authorOverride')->getData()) { + $feedback->setAuthor($author->getFullName()); } $this->entityManager->persist($feedback); diff --git a/src/Controller/Administrative/Feedback/ProvideController.php b/src/Controller/Administrative/Feedback/ProvideController.php index b6027a7..bb95a6d 100644 --- a/src/Controller/Administrative/Feedback/ProvideController.php +++ b/src/Controller/Administrative/Feedback/ProvideController.php @@ -56,6 +56,12 @@ class ProvideController extends AbstractController $feedback->setStatus(Feedback::STATUS_PUBLISHED); } + if (true === $form->has('author')) { + /** @var User $author */ + $author = $form->get('author')->getData(); + $feedback->setAuthor($author->getFullName()); + } + $teamer = $disposition->getTeamer(); $teamer->addFeedback($feedback); $disposition->setFeedback($feedback); diff --git a/src/Form/AbstractFeedbackType.php b/src/Form/AbstractFeedbackType.php new file mode 100644 index 0000000..6cfddf3 --- /dev/null +++ b/src/Form/AbstractFeedbackType.php @@ -0,0 +1,51 @@ +add('ratings', FeedbackRatingsType::class, [ + 'label' => false, + 'feedback_set' => $feedbackSet, + ]) + ->add('comment', TextareaType::class, [ + 'label' => 'Kommentar (öffentlich)', + 'required' => false, + 'attr' => [ + 'rows' => 5, + ], + 'help' => 'Bitte formuliere deinen Kommentar so, dass er an das Team weitergegeben werden kann', + ]) + ->add('commentInternal', TextareaType::class, [ + 'label' => 'Kommentar (intern)', + 'required' => false, + 'attr' => [ + 'rows' => 5, + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Feedback::class, + ]); + } +} diff --git a/src/Form/AdminFeedbackType.php b/src/Form/AdminFeedbackType.php index a972248..bed15e9 100644 --- a/src/Form/AdminFeedbackType.php +++ b/src/Form/AdminFeedbackType.php @@ -2,21 +2,17 @@ namespace App\Form; -use App\Entity\Feedback; use App\Entity\FeedbackSet; use App\Entity\Teamer; use App\Entity\User; use App\Repository\FeedbackSetRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; -use Symfony\Component\Form\AbstractType; -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 AdminFeedbackType extends AbstractType +class AdminFeedbackType extends AbstractFeedbackType { public function __construct(private readonly FeedbackSetRepository $feedbackSetRepository) { @@ -34,7 +30,7 @@ class AdminFeedbackType extends AbstractType ]) ->add('authorOverride', AutocompleteEntityType::class, [ 'mapped' => false, - 'label' => 'abweichender Absender', + 'label' => 'Feedbackgeber:in', 'class' => User::class, 'label_property' => 'lastName', 'label_function' => fn (User $user) => (string) $user, @@ -57,7 +53,7 @@ class AdminFeedbackType extends AbstractType return; } - $this->addDynamicForm($feedbackSet, $form); + $this->addFeedbackFields($form, $feedbackSet); }) ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { $data = $event->getData(); @@ -67,47 +63,26 @@ class AdminFeedbackType extends AbstractType return; } - $feedbackSet = $this->feedbackSetRepository->find($data['feedbackSet']); - $this->addDynamicForm($feedbackSet, $form); - }) - ; - } + if (null === $feedbackSet = $this->feedbackSetRepository->find($data['feedbackSet'])) { + return; + } - private function addDynamicForm(FeedbackSet $feedbackSet, FormInterface $form): void - { - $form - ->add('ratings', FeedbackRatingsType::class, [ - 'label' => false, - 'feedback_set' => $feedbackSet, - ]) - ->add('comment', TextareaType::class, [ - 'label' => 'Kommentar (öffentlich)', - 'required' => false, - 'attr' => [ - 'rows' => 5, - ], - 'help' => 'Bitte formuliere deinen Kommentar so, dass er an das Team weitergegeben werden kann', - ]) - ->add('commentInternal', TextareaType::class, [ - 'label' => 'Kommentar (intern)', - 'required' => false, - 'attr' => [ - 'rows' => 5, - ], - ]) + $this->addFeedbackFields($form, $feedbackSet); + }) ; } public function configureOptions(OptionsResolver $resolver): void { - $resolver - ->setDefaults([ - 'data_class' => Feedback::class, - 'validation_groups' => [ - 'Default', - 'admin', - ], - ]) - ; + parent::configureOptions($resolver); + + $resolver->setDefaults([ + // the disposition-driven flow guarantees teamer and feedback set structurally, here + // they are picked by hand and must be validated + 'validation_groups' => [ + 'Default', + 'admin', + ], + ]); } } diff --git a/src/Form/FeedbackType.php b/src/Form/FeedbackType.php index 63f4e99..32bf093 100644 --- a/src/Form/FeedbackType.php +++ b/src/Form/FeedbackType.php @@ -2,46 +2,70 @@ namespace App\Form; -use App\Entity\Feedback; use App\Entity\FeedbackSet; -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use App\Entity\User; +use App\Repository\UserRepository; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints as Assert; -class FeedbackType extends AbstractType +class FeedbackType extends AbstractFeedbackType { + public function __construct( + private readonly Security $security, + private readonly UserRepository $userRepository, + ) { + } + public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder - ->add('ratings', FeedbackRatingsType::class, [ - 'label' => false, - 'feedback_set' => $options['feedback_set'], - ]) - ->add('comment', TextareaType::class, [ - 'label' => 'Kommentar (öffentlich)', - 'required' => false, - 'attr' => [ - 'rows' => 5, - ], - 'help' => 'Bitte formuliere deinen Kommentar so, dass er an das Team weitergegeben werden kann', - ]) - ->add('commentInternal', TextareaType::class, [ - 'label' => 'Kommentar (intern)', - 'required' => false, - 'attr' => [ - 'rows' => 5, - ], - ]) - ; + if (true === $this->security->isGranted('ROLE_ADMINISTRATIVE')) { + $this->addAuthorField($builder); + } + + $this->addFeedbackFields($builder, $options['feedback_set']); + } + + /** + * Feedback::$author is a name snapshot rather than a relation, so the field stays unmapped and + * the controller writes the chosen user's full name. + */ + private function addAuthorField(FormBuilderInterface $builder): void + { + $roles = array_diff(array_keys(User::ROLES), ['ROLE_TEAMER']); + + $choices = $this->userRepository->getUsersByRoles($roles); + + /** @var User $user */ + $user = $this->security->getUser(); + + // a user granted the role by the hierarchy alone would not be among the choices + $choiceIds = array_map(fn (User $choice) => $choice->getId(), $choices); + + if (false === in_array($user->getId(), $choiceIds, true)) { + array_unshift($choices, $user); + } + + $builder->add('author', EntityType::class, [ + 'label' => 'Feedbackgeber:in', + 'mapped' => false, + 'class' => User::class, + 'choices' => $choices, + 'choice_label' => 'fullName', + 'data' => $user, + 'constraints' => [ + new Assert\NotNull(message: 'Bitte wähle eine:n Feedbackgeber:in'), + ], + ]); } public function configureOptions(OptionsResolver $resolver): void { + parent::configureOptions($resolver); + $resolver - ->setDefaults([ - 'data_class' => Feedback::class, - ]) ->setRequired(['feedback_set']) ->setAllowedTypes('feedback_set', FeedbackSet::class) ; diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 954f977..bfa60b4 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -61,6 +61,35 @@ class UserRepository extends ServiceEntityRepository ; } + /** + * Active users holding at least one of the given roles. + * + * @param string[] $roles + * + * @return User[] + */ + public function getUsersByRoles(array $roles): array + { + $qb = $this->createQueryBuilder('user'); + + $roleMatches = $qb->expr()->orX(); + + foreach ($roles as $index => $role) { + $roleMatches->add('JSON_CONTAINS(user.roles, :role_'.$index.') = 1'); + $qb->setParameter('role_'.$index, json_encode($role)); + } + + return $qb + ->andWhere($roleMatches) + ->andWhere($qb->expr()->isNull('user.deletedAt')) + ->andWhere($qb->expr()->isNull('user.disabledAt')) + ->orderBy('user.lastName', 'ASC') + ->addOrderBy('user.firstName', 'ASC') + ->getQuery() + ->getResult() + ; + } + /** * @param string[]|string $hotelCode * diff --git a/templates/_partials/_feedback_rating_scale.html.twig b/templates/_partials/_feedback_rating_scale.html.twig new file mode 100644 index 0000000..3d4657c --- /dev/null +++ b/templates/_partials/_feedback_rating_scale.html.twig @@ -0,0 +1,53 @@ +

+ Bewertungsskala +

+ + + + + + + + + + + + + + + + + + + + + +
+ 5 Punkte + + Der/die Wochenteamer:in hat hervorragende Arbeit geleistet und ist beim Team und den Gästen + sehr gut angekommen, es gibt nichts auszusetzen. +
+ 4 Punkte + + Der/die Wochenteamer:in hat einen sehr guten Job geleistet. Wenn es etwas zu beanstanden oder + Ideen zur Verbesserung gibt, dann sind es nur Kleinigkeiten (diese gerne in den Kommentar + schreiben). +
+ 3 Punkte + + Der/die Wochenteamer:in hat einen ganz guten Job erledigt. Es gibt einige Dinge, die + verbesserungswürdig sind (diese gerne in den Kommentaren vermerken). Der/die Wochenteamer:in + ist aber nicht negativ aufgefallen. +
+ 2 Punkte + + Der/die Wocheteamer:in hat keinen guten Job geleistet. Der Einsatz hat das Team eher belastet, + als es zu unterstützen. Der/die Wochenteamer:in bekommt noch eine Chance sich zu beweisen + (hierfür gerne ein schriftliches Feedback im Kommentarfeld hinterlassen). +
+ 1 Punkt + + Der/die Wochenteamer:in hat einen schlechten Job erledigt, ist Team und/oder Gästen negativ + aufgefallen und soll keine weiteren Einsätze erhalten. +
diff --git a/templates/admin/feedback/provide.html.twig b/templates/admin/feedback/provide.html.twig index 305795e..5de51a2 100644 --- a/templates/admin/feedback/provide.html.twig +++ b/templates/admin/feedback/provide.html.twig @@ -51,59 +51,7 @@ {{ form_end(form) }}
-

- Bewertungsskala -

- - - - - - - - - - - - - - - - - - - - - -
- 5 Punkte - - Der/die Wochenteamer:in hat hervorragende Arbeit geleistet und ist beim Team und den Gästen - sehr gut angekommen, es gibt nichts auszusetzen. -
- 4 Punkte - - Der/die Wochenteamer:in hat einen sehr guten Job geleistet. Wenn es etwas zu beanstanden oder - Ideen zur Verbesserung gibt, dann sind es nur Kleinigkeiten (diese gerne in den Kommentar - schreiben). -
- 3 Punkte - - Der/die Wochenteamer:in hat einen ganz guten Job erledigt. Es gibt einige Dinge, die - verbesserungswürdig sind (diese gerne in den Kommentaren vermerken). Der/die Wochenteamer:in - ist aber nicht negativ aufgefallen. -
- 2 Punkte - - Der/die Wocheteamer:in hat keinen guten Job geleistet. Der Einsatz hat das Team eher belastet, - als es zu unterstützen. Der/die Wochenteamer:in bekommt noch eine Chance sich zu beweisen - (hierfür gerne ein schriftliches Feedback im Kommentarfeld hinterlassen). -
- 1 Punkt - - Der/die Wochenteamer:in hat einen schlechten Job erledigt, ist Team und/oder Gästen negativ - aufgefallen und soll keine weiteren Einsätze erhalten. -
+ {% include '_partials/_feedback_rating_scale.html.twig' %}
{% endblock %} \ No newline at end of file diff --git a/templates/administrative/feedback/provide.html.twig b/templates/administrative/feedback/provide.html.twig index 63bd6c3..2b85d63 100644 --- a/templates/administrative/feedback/provide.html.twig +++ b/templates/administrative/feedback/provide.html.twig @@ -13,6 +13,11 @@
{{ form_start(form) }}
+ {% if form.author is defined %} +
+ {{ form_row(form.author) }} +
+ {% endif %} {% for child in form.ratings.children %}

@@ -35,59 +40,7 @@ {{ form_end(form) }}

-

- Bewertungsskala -

- - - - - - - - - - - - - - - - - - - - - -
- 5 Punkte - - Der/die Wochenteamer:in hat hervorragende Arbeit geleistet und ist beim Team und den Gästen - sehr gut angekommen, es gibt nichts auszusetzen. -
- 4 Punkte - - Der/die Wochenteamer:in hat einen sehr guten Job geleistet. Wenn es etwas zu beanstanden oder - Ideen zur Verbesserung gibt, dann sind es nur Kleinigkeiten (diese gerne in den Kommentar - schreiben). -
- 3 Punkte - - Der/die Wochenteamer:in hat einen ganz guten Job erledigt. Es gibt einige Dinge, die - verbesserungswürdig sind (diese gerne in den Kommentaren vermerken). Der/die Wochenteamer:in - ist aber nicht negativ aufgefallen. -
- 2 Punkte - - Der/die Wocheteamer:in hat keinen guten Job geleistet. Der Einsatz hat das Team eher belastet, - als es zu unterstützen. Der/die Wochenteamer:in bekommt noch eine Chance sich zu beweisen - (hierfür gerne ein schriftliches Feedback im Kommentarfeld hinterlassen). -
- 1 Punkt - - Der/die Wochenteamer:in hat einen schlechten Job erledigt, ist Team und/oder Gästen negativ - aufgefallen und soll keine weiteren Einsätze erhalten. -
+ {% include '_partials/_feedback_rating_scale.html.twig' %}
{% endblock %} \ No newline at end of file