WIP: Implement feedback functionality
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use App\Entity\Feedback;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20231105160706 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE feedback ADD status VARCHAR(32) NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->connection->executeQuery('UPDATE feedback SET status=?', [Feedback::STATUS_NEW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE feedback DROP status');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,19 +3,45 @@
|
|||||||
namespace App\Controller\Admin\Teamer;
|
namespace App\Controller\Admin\Teamer;
|
||||||
|
|
||||||
use App\Entity\Teamer;
|
use App\Entity\Teamer;
|
||||||
|
use App\Repository\DispositionRepository;
|
||||||
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
class RecentAssignmentsController extends AbstractController
|
class RecentAssignmentsController extends AbstractController
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DispositionRepository $dispositionRepository,
|
||||||
|
private readonly PaginatorInterface $paginator
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
#[Route('/admin/teamer/recent-assignments/{uuid}', name: 'app_admin_teamer_recent_assignments')]
|
#[Route('/admin/teamer/recent-assignments/{uuid}', name: 'app_admin_teamer_recent_assignments')]
|
||||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||||
public function index(Teamer $teamer): Response
|
public function index(Teamer $teamer, Request $request): Response
|
||||||
{
|
{
|
||||||
|
$query = $this
|
||||||
|
->dispositionRepository
|
||||||
|
->getRecentQuery($teamer)
|
||||||
|
;
|
||||||
|
|
||||||
|
$pagination = $this->paginator->paginate(
|
||||||
|
$query,
|
||||||
|
$request->query->getInt('page', 1),
|
||||||
|
10,
|
||||||
|
[
|
||||||
|
'defaultSortFieldName' => 'destination.dateFrom',
|
||||||
|
'defaultSortDirection' => 'asc',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return $this->render('admin/teamer/recent_assignments.html.twig', [
|
return $this->render('admin/teamer/recent_assignments.html.twig', [
|
||||||
'teamer' => $teamer,
|
'teamer' => $teamer,
|
||||||
|
'pagination' => $pagination,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,13 @@ class ProvideController extends AbstractController
|
|||||||
{
|
{
|
||||||
$assignment = $disposition->getAssignment();
|
$assignment = $disposition->getAssignment();
|
||||||
$destination = $assignment->getDestination();
|
$destination = $assignment->getDestination();
|
||||||
$feebackSet = $assignment
|
$feedbackSet = $assignment
|
||||||
->getJobProfile()
|
->getJobProfile()
|
||||||
->getFeedbackSet()
|
->getFeedbackSet()
|
||||||
;
|
;
|
||||||
$feedback = new Feedback();
|
$feedback = new Feedback();
|
||||||
|
|
||||||
$form = $this->createForm(FeedbackType::class, $feedback, ['feedback_set' => $feebackSet]);
|
$form = $this->createForm(FeedbackType::class, $feedback, ['feedback_set' => $feedbackSet]);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
|||||||
use BlameableEntity;
|
use BlameableEntity;
|
||||||
use TimestampableEntity;
|
use TimestampableEntity;
|
||||||
|
|
||||||
|
public const STATUS_NEW = 'new';
|
||||||
|
public const STATUS_PUBLISHED = 'published';
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
@@ -23,6 +26,9 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
|||||||
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
||||||
private string $uuid;
|
private string $uuid;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 32)]
|
||||||
|
private ?string $status = self::STATUS_NEW;
|
||||||
|
|
||||||
#[ORM\ManyToOne(inversedBy: 'feedback')]
|
#[ORM\ManyToOne(inversedBy: 'feedback')]
|
||||||
private ?Teamer $teamer = null;
|
private ?Teamer $teamer = null;
|
||||||
|
|
||||||
@@ -56,6 +62,18 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
|||||||
return $this->uuid;
|
return $this->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStatus(): ?string
|
||||||
|
{
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatus(string $status): static
|
||||||
|
{
|
||||||
|
$this->status = $status;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTeamer(): ?Teamer
|
public function getTeamer(): ?Teamer
|
||||||
{
|
{
|
||||||
return $this->teamer;
|
return $this->teamer;
|
||||||
@@ -104,6 +122,21 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAverageRating(): ?int
|
||||||
|
{
|
||||||
|
if (0 === count($this->ratings)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sum = 0;
|
||||||
|
|
||||||
|
foreach ($this->ratings as $rating) {
|
||||||
|
$sum += $rating['mark'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return round($sum/count($this->ratings));
|
||||||
|
}
|
||||||
|
|
||||||
public function getComment(): ?string
|
public function getComment(): ?string
|
||||||
{
|
{
|
||||||
return $this->comment;
|
return $this->comment;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Form\DataTransformer\EntityToIdTransformer;
|
use App\Form\DataTransformer\EntityToIdentifierTransformer;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@@ -21,15 +21,19 @@ class AutocompleteEntityType extends AbstractType
|
|||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$dataTransformer = new EntityToIdTransformer($this->entityManager, $options['class']);
|
$dataTransformer = new EntityToIdentifierTransformer(
|
||||||
|
$this->entityManager,
|
||||||
|
$options['class'],
|
||||||
|
$options['identifier']
|
||||||
|
);
|
||||||
$builder->addModelTransformer($dataTransformer);
|
$builder->addModelTransformer($dataTransformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
{
|
{
|
||||||
$resolver->setRequired(['class']);
|
$resolver
|
||||||
|
->setRequired(['class'])
|
||||||
$resolver->setDefaults([
|
->setDefaults([
|
||||||
'endpoint_route' => null,
|
'endpoint_route' => null,
|
||||||
'choices' => [],
|
'choices' => [],
|
||||||
'compound' => false,
|
'compound' => false,
|
||||||
@@ -38,10 +42,12 @@ class AutocompleteEntityType extends AbstractType
|
|||||||
'placeholder' => null,
|
'placeholder' => null,
|
||||||
'endpoint_parameters' => [],
|
'endpoint_parameters' => [],
|
||||||
'controller_action' => null,
|
'controller_action' => null,
|
||||||
]);
|
'identifier' => 'id',
|
||||||
|
])
|
||||||
$resolver->setAllowedTypes('choices', 'array');
|
->setAllowedTypes('choices', 'array')
|
||||||
$resolver->setAllowedTypes('label_function', ['null', 'callable']);
|
->setAllowedTypes('label_function', ['null', 'callable'])
|
||||||
|
->setAllowedValues('identifier', ['id', 'uuid'])
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
@@ -57,17 +63,17 @@ class AutocompleteEntityType extends AbstractType
|
|||||||
$view->vars['initial_label'] = '';
|
$view->vars['initial_label'] = '';
|
||||||
$view->vars['action'] = $options['controller_action'];
|
$view->vars['action'] = $options['controller_action'];
|
||||||
|
|
||||||
$entity = $form->getData();
|
if (null === $entity = $form->getData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== $entity) {
|
|
||||||
if (null !== $options['label_function']) {
|
if (null !== $options['label_function']) {
|
||||||
$labelFunction = $options['label_function'];
|
$labelFunction = $options['label_function'];
|
||||||
$view->vars['initial_label'] = $labelFunction($entity);
|
$view->vars['initial_label'] = $labelFunction($entity);
|
||||||
} else {
|
} else {
|
||||||
$getter = 'get'.ucfirst($options['label_property']);
|
$getter = 'get'.ucfirst($options['label_property']);
|
||||||
if (method_exists($entity, $getter)) {
|
if (method_exists($entity, $getter)) {
|
||||||
$view->vars['initial_label'] = $entity->$getter();
|
$view->vars['initial_label'] = call_user_func([$entity, $getter]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\DataTransformer;
|
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Symfony\Component\Form\DataTransformerInterface;
|
|
||||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
|
||||||
|
|
||||||
class EntityToIdTransformer implements DataTransformerInterface
|
|
||||||
{
|
|
||||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly string $class)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function transform($value)
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reverseTransform($value)
|
|
||||||
{
|
|
||||||
if (empty($value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$entity = $this->entityManager->getRepository($this->class)->find($value);
|
|
||||||
|
|
||||||
if (null === $entity) {
|
|
||||||
throw new TransformationFailedException(sprintf('No %s with id %d found', $this->class, $value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\DataTransformer;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
|
||||||
|
class EntityToIdentifierTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
private string $identifier;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly EntityManagerInterface $entityManager,
|
||||||
|
private readonly string $class,
|
||||||
|
string $identifier = 'id'
|
||||||
|
) {
|
||||||
|
if (false === in_array($identifier, ['id', 'uuid'])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Identifier must be one of "id" or "uuid" got "%s"', $identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->identifier = $identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform($value)
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$getter = 'get'.ucfirst($this->identifier);
|
||||||
|
|
||||||
|
return call_user_func([$value, $getter]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransform($value)
|
||||||
|
{
|
||||||
|
if (empty($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity = $this
|
||||||
|
->entityManager
|
||||||
|
->getRepository($this->class)
|
||||||
|
->findOneBy([$this->identifier => $value])
|
||||||
|
;
|
||||||
|
|
||||||
|
if (null === $entity) {
|
||||||
|
throw new TransformationFailedException(sprintf('No %s with %s %s found', $this->class, $this->identifier, $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\DataTransformer;
|
||||||
|
|
||||||
|
use App\Entity\FeedbackSet;
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
|
||||||
|
class FeedbackToRatingsTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
public function __construct(private readonly FeedbackSet $feedbackSet)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform(mixed $value)
|
||||||
|
{
|
||||||
|
// Not in use
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransform(mixed $value)
|
||||||
|
{
|
||||||
|
$feedbackRatings = [];
|
||||||
|
|
||||||
|
foreach ($this->feedbackSet->getRatings() as $index => $rating) {
|
||||||
|
$feedbackRatings[] = [
|
||||||
|
'rating' => $rating,
|
||||||
|
'mark' => $value['rating_'.$index],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $feedbackRatings;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Form;
|
|||||||
|
|
||||||
use App\Entity\Feedback;
|
use App\Entity\Feedback;
|
||||||
use App\Entity\FeedbackSet;
|
use App\Entity\FeedbackSet;
|
||||||
|
use App\Form\DataTransformer\FeedbackToRatingsTransformer;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@@ -26,6 +27,13 @@ class FeedbackType extends AbstractType
|
|||||||
],
|
],
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// This is most probably more suitable for DatamapperInterface
|
||||||
|
// but it works very well for the time being
|
||||||
|
$builder
|
||||||
|
->get('ratings')
|
||||||
|
->addModelTransformer(new FeedbackToRatingsTransformer($options['feedback_set']))
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Form\DataTransformer\EntityToIdTransformer;
|
use App\Form\DataTransformer\EntityToIdentifierTransformer;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
@@ -19,7 +19,11 @@ class HiddenEntityType extends AbstractType
|
|||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$transformer = new EntityToIdTransformer($this->entityManager, $options['class']);
|
$transformer = new EntityToIdentifierTransformer(
|
||||||
|
$this->entityManager,
|
||||||
|
$options['class'],
|
||||||
|
$options['identifier']
|
||||||
|
);
|
||||||
$builder->addModelTransformer($transformer);
|
$builder->addModelTransformer($transformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +38,9 @@ class HiddenEntityType extends AbstractType
|
|||||||
->setRequired(['class'])
|
->setRequired(['class'])
|
||||||
->setDefaults([
|
->setDefaults([
|
||||||
'invalid_message' => 'The entity does not exist.',
|
'invalid_message' => 'The entity does not exist.',
|
||||||
|
'identifier' => 'id',
|
||||||
])
|
])
|
||||||
|
->setAllowedValues('identifier', ['id', 'uuid'])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,11 @@ class DispositionRepository extends ServiceEntityRepository
|
|||||||
$qb = $this->createQueryBuilder('disposition');
|
$qb = $this->createQueryBuilder('disposition');
|
||||||
|
|
||||||
return $qb
|
return $qb
|
||||||
->select('disposition', 'assignment', 'job_profile', 'destination')
|
->select('disposition', 'assignment', 'job_profile', 'destination', 'feedback')
|
||||||
->innerJoin('disposition.assignment', 'assignment')
|
->innerJoin('disposition.assignment', 'assignment')
|
||||||
->innerJoin('assignment.jobProfile', 'job_profile')
|
->innerJoin('assignment.jobProfile', 'job_profile')
|
||||||
->innerJoin('assignment.destination', 'destination')
|
->innerJoin('assignment.destination', 'destination')
|
||||||
|
->leftJoin('disposition.feedback', 'feedback')
|
||||||
->where($qb->expr()->andX(
|
->where($qb->expr()->andX(
|
||||||
$qb->expr()->eq('disposition.teamer', ':teamer'),
|
$qb->expr()->eq('disposition.teamer', ':teamer'),
|
||||||
$qb->expr()->orX(
|
$qb->expr()->orX(
|
||||||
@@ -99,12 +100,14 @@ class DispositionRepository extends ServiceEntityRepository
|
|||||||
$qb = $this->createQueryBuilder('disposition');
|
$qb = $this->createQueryBuilder('disposition');
|
||||||
|
|
||||||
return $qb
|
return $qb
|
||||||
->select('disposition', 'assignment', 'destination')
|
->select('disposition', 'assignment', 'destination', 'feedback')
|
||||||
->innerJoin('disposition.assignment', 'assignment')
|
->innerJoin('disposition.assignment', 'assignment')
|
||||||
->innerJoin('assignment.destination', 'destination')
|
->innerJoin('assignment.destination', 'destination')
|
||||||
|
->leftJoin('disposition.feedback', 'feedback')
|
||||||
->where($qb->expr()->eq('disposition.teamer', ':teamer'))
|
->where($qb->expr()->eq('disposition.teamer', ':teamer'))
|
||||||
->setParameter('teamer', $teamer)
|
->setParameter('teamer', $teamer)
|
||||||
->orderBy('destination.dateFrom', 'DESC')
|
->orderBy('destination.dateFrom', 'DESC')
|
||||||
|
->setMaxResults($limit)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getResult()
|
->getResult()
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class DispositionVoter extends Voter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return in_array($attribute, [static::VIEW, static::EDIT, static::DELETE, static::CONTRACT, static::INVOICE]);
|
return in_array($attribute, [static::VIEW, static::EDIT, static::DELETE, static::CONTRACT, static::INVOICE, static::FEEDBACK]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Entity\Feedback;
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
|
class FeedbackVoter extends Voter
|
||||||
|
{
|
||||||
|
public const VIEW = 'VIEW';
|
||||||
|
public const PUBLISH = 'PUBLISH';
|
||||||
|
public const DELETE = 'DELETE';
|
||||||
|
|
||||||
|
protected function supports(string $attribute, mixed $subject): bool
|
||||||
|
{
|
||||||
|
return $subject instanceof Feedback && in_array($attribute, [static::VIEW, static::PUBLISH, static::DELETE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
/** @var Feedback $feedback */
|
||||||
|
$feedback = $subject;
|
||||||
|
|
||||||
|
if (in_array('ROLE_ADMIN', $token->getRoleNames())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('ROLE_HOUSE_MANAGER', $token->getRoleNames())) {
|
||||||
|
return static::VIEW === $attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('ROLE_TEAMER', $token->getRoleNames())) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $token->getUser();
|
||||||
|
$teamer = $user->getTeamer();
|
||||||
|
|
||||||
|
return $feedback->getTeamer() === $teamer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,12 +70,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#">
|
<a href="{{ path('app_admin_teamer_profile', { 'uuid': teamer.uuid }) }}">
|
||||||
<span class="whitespace-nowrap">{{ teamer.lastName }}</span>
|
<span class="whitespace-nowrap">{{ teamer.lastName }}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" class="whitespace-nowrap">
|
<a href="{{ path('app_admin_teamer_profile', { 'uuid': teamer.uuid }) }}" class="whitespace-nowrap">
|
||||||
{{ teamer.firstName }}
|
{{ teamer.firstName }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -11,6 +11,65 @@
|
|||||||
<h1 class="text-2xl font-bold pb-8">
|
<h1 class="text-2xl font-bold pb-8">
|
||||||
Vergangene Einsätze & Feeback {{ teamer }}
|
Vergangene Einsätze & Feeback {{ teamer }}
|
||||||
</h1>
|
</h1>
|
||||||
|
<div class="data-table-wrapper">
|
||||||
|
<div class="data-table-wrapper__inner">
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Einsatz
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Gesamtnote
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Kommentar
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for disposition in pagination %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ disposition.assignment.destination.dateFrom|date('d.m.Y') }}
|
||||||
|
<br>
|
||||||
|
{{ disposition.assignment.destination.product }}
|
||||||
|
<br>
|
||||||
|
{{ disposition.assignment.destination.hotel }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if disposition.feedback %}
|
||||||
|
{{ disposition.feedback.averageRating }}
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if disposition.feedback %}
|
||||||
|
<div class="truncate">
|
||||||
|
{{ disposition.feedback.comment|default('-') }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button>
|
||||||
|
{{ icon('feedback') }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<td colspan="7">
|
||||||
|
Keine Daten...
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ knp_pagination_render(pagination) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user