WIP: Implement role house manager and feedback providing
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="icon-feedback" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" />
|
||||
</symbol>
|
||||
<symbol id="icon-login" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
|
||||
</symbol>
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -85,6 +85,14 @@ services:
|
||||
method: createMainMenu
|
||||
alias: teamer_main
|
||||
|
||||
App\Menu\HouseManagerMenuBuilder:
|
||||
arguments:
|
||||
$factory: '@knp_menu.factory'
|
||||
tags:
|
||||
- name: knp_menu.menu_builder
|
||||
method: createMainMenu
|
||||
alias: house_manager_main
|
||||
|
||||
App\Service\Upload\UploadHandler:
|
||||
arguments:
|
||||
$orphanageManager: '@oneup_uploader.orphanage_manager'
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231104170014 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 disposition DROP FOREIGN KEY FK_4C58BF60D249A887');
|
||||
$this->addSql('ALTER TABLE disposition ADD CONSTRAINT FK_4C58BF60D249A887 FOREIGN KEY (feedback_id) REFERENCES feedback (id) ON DELETE SET NULL');
|
||||
$this->addSql('ALTER TABLE feedback ADD comment LONGTEXT DEFAULT NULL, ADD comment_public TINYINT(1) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE disposition DROP FOREIGN KEY FK_4C58BF60D249A887');
|
||||
$this->addSql('ALTER TABLE disposition ADD CONSTRAINT FK_4C58BF60D249A887 FOREIGN KEY (feedback_id) REFERENCES feedback (id)');
|
||||
$this->addSql('ALTER TABLE feedback DROP comment, DROP comment_public');
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,16 @@ class HotelDataProvider
|
||||
return $this->getAll()[$busProId] ?? null;
|
||||
}
|
||||
|
||||
public function findByCode(string $code): ?Hotel
|
||||
public function findByCode(string $code): array
|
||||
{
|
||||
$hotels = [];
|
||||
|
||||
foreach ($this->getAll() as $hotel) {
|
||||
if ($code === $hotel->getCode()) {
|
||||
return $hotel;
|
||||
if (str_starts_with($hotel->getCode(), $code)) {
|
||||
$hotels[] = $hotel;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return $hotels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\HouseManager\Feedback;
|
||||
|
||||
use App\BusProNet\DataProvider\HotelDataProvider;
|
||||
use App\BusProNet\Model\Hotel;
|
||||
use App\Entity\User;
|
||||
use App\Repository\DispositionRepository;
|
||||
use Knp\Component\Pager\PaginatorInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class IndexController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DispositionRepository $dispositionRepository,
|
||||
private readonly HotelDataProvider $hotelDataProvider,
|
||||
private readonly PaginatorInterface $paginator
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/house-manager/pending-feedback', name: 'app_house_manager_feedback_index')]
|
||||
#[IsGranted('ROLE_HOUSE_MANAGER')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
$hotels = $this
|
||||
->hotelDataProvider
|
||||
->findByCode($user->getHotelCode())
|
||||
;
|
||||
$hotelBusProIds = array_map(function (Hotel $hotel) {
|
||||
return $hotel->getBusProId();
|
||||
}, $hotels);
|
||||
|
||||
$query = $this
|
||||
->dispositionRepository
|
||||
->getPendingFeedbackQuery($hotelBusProIds)
|
||||
;
|
||||
|
||||
$pagination = $this->paginator->paginate(
|
||||
$query,
|
||||
$request->query->getInt('page', 1),
|
||||
10,
|
||||
[
|
||||
'defaultSortFieldName' => 'assignment.dateFrom',
|
||||
'defaultSortDirection' => 'asc',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->render('house_manager/feedback/index.html.twig', [
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\HouseManager\Feedback;
|
||||
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Feedback;
|
||||
use App\Event\FeedbackProvidedEvent;
|
||||
use App\Form\FeedbackType;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class ProvideController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/house-manager/feedback/provide/{uuid}', name: 'app_house_manager_feedback_provide')]
|
||||
#[IsGranted('FEEDBACK', subject: 'disposition')]
|
||||
public function index(Disposition $disposition, Request $request): Response
|
||||
{
|
||||
$assignment = $disposition->getAssignment();
|
||||
$destination = $assignment->getDestination();
|
||||
$feebackSet = $assignment
|
||||
->getJobProfile()
|
||||
->getFeedbackSet()
|
||||
;
|
||||
$feedback = new Feedback();
|
||||
|
||||
$form = $this->createForm(FeedbackType::class, $feedback, ['feedback_set' => $feebackSet]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$feedback
|
||||
->setAssignmentDestination($destination)
|
||||
->setAssignmentDate($assignment->getEffectivePeriod()->start->toDateTimeImmutable())
|
||||
;
|
||||
$teamer = $disposition->getTeamer();
|
||||
$teamer->addFeedback($feedback);
|
||||
$disposition->setFeedback($feedback);
|
||||
|
||||
$this->entityManager->persist($feedback);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->eventDispatcher->dispatch(new FeedbackProvidedEvent($feedback), FeedbackProvidedEvent::NAME);
|
||||
$this->logger->info('Feedback provided', [
|
||||
'feedback_id' => $feedback->getId(),
|
||||
'teamer' => (string) $teamer,
|
||||
'destination' => (string) $destination,
|
||||
]);
|
||||
|
||||
$this->addFlash('success', 'Das Feedback wurde entgegengenommen');
|
||||
|
||||
return $this->redirectToRoute('app_house_manager_feedback_index');
|
||||
}
|
||||
|
||||
return $this->render('house_manager/feedback/provide.html.twig', [
|
||||
'form' => $form,
|
||||
'disposition' => $disposition,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,6 @@ class IndexController extends AbstractController
|
||||
#[Route('/house-manager', name: 'app_house_manager_index')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('');
|
||||
return $this->render('house_manager/index.html.twig');
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
|
||||
private Collection $documents;
|
||||
|
||||
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
|
||||
#[ORM\JoinColumn(onDelete: 'SET NULL')]
|
||||
private ?Feedback $feedback = null;
|
||||
|
||||
public function __construct(Application $application)
|
||||
|
||||
@@ -35,6 +35,12 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
||||
#[ORM\Column]
|
||||
private array $ratings = [];
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
private ?string $comment = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $commentPublic = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->uuid = Uuid::v4();
|
||||
@@ -97,4 +103,28 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getComment(): ?string
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
public function setComment(?string $comment): static
|
||||
{
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isCommentPublic(): ?bool
|
||||
{
|
||||
return $this->commentPublic;
|
||||
}
|
||||
|
||||
public function setCommentPublic(bool $commentPublic): static
|
||||
{
|
||||
$this->commentPublic = $commentPublic;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Feedback;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class FeedbackProvidedEvent extends Event
|
||||
{
|
||||
public const NAME = 'feedback.provided';
|
||||
|
||||
public function __construct(private readonly Feedback $feedback)
|
||||
{
|
||||
}
|
||||
|
||||
public function getFeedback(): Feedback
|
||||
{
|
||||
return $this->feedback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\FeedbackSet;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class FeedbackRatingsType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$ratings = $options['feedback_set']->getRatings();
|
||||
|
||||
foreach ($ratings as $index => $rating) {
|
||||
$builder->add('rating_'.$index, ChoiceType::class, [
|
||||
'label' => $rating,
|
||||
'choices' => array_combine(range(1, 6), range(1,6)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver
|
||||
->setRequired(['feedback_set'])
|
||||
->setAllowedTypes('feedback_set', FeedbackSet::class)
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
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 Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class FeedbackType extends AbstractType
|
||||
{
|
||||
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',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'rows' => 5,
|
||||
],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver
|
||||
->setDefaults([
|
||||
'data_class' => Feedback::class,
|
||||
])
|
||||
->setRequired(['feedback_set'])
|
||||
->setAllowedTypes('feedback_set', FeedbackSet::class)
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,17 @@ abstract class AbstractMenuBuilder
|
||||
}
|
||||
}
|
||||
|
||||
protected function addHouseManagerItem(ItemInterface $menu): void
|
||||
{
|
||||
if ($this->security->isGranted('ROLE_HOUSE_MANAGER')) {
|
||||
$menu
|
||||
->addChild('zum Hausmanagerbereich', ['route' => 'app_house_manager_index'])
|
||||
->setChildrenAttribute('title', 'zum Hausmanagerbereich')
|
||||
->setExtra('icon', 'user')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
protected function addLogoutItem(ItemInterface $menu): void
|
||||
{
|
||||
$token = $this->security->getToken();
|
||||
|
||||
@@ -39,7 +39,7 @@ class AdminMenuBuilder extends AbstractMenuBuilder
|
||||
],
|
||||
[
|
||||
'route' => 'app_admin_teamer_index',
|
||||
'title' => 'Teamer:innenübersicht',
|
||||
'title' => 'Teamübersicht',
|
||||
'icon' => 'users',
|
||||
'hideChildren' => true,
|
||||
'children' => $this->getTeamerMenuItems(),
|
||||
@@ -165,6 +165,10 @@ class AdminMenuBuilder extends AbstractMenuBuilder
|
||||
|
||||
$this->addDivider($menu);
|
||||
|
||||
$this->addHouseManagerItem($menu);
|
||||
|
||||
$this->addDivider($menu);
|
||||
|
||||
$this->addLogoutItem($menu);
|
||||
|
||||
return $menu;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Menu;
|
||||
|
||||
use Knp\Menu\ItemInterface;
|
||||
|
||||
class HouseManagerMenuBuilder extends AbstractMenuBuilder
|
||||
{
|
||||
public function createMainMenu(array $options): ItemInterface
|
||||
{
|
||||
$menuItems = [
|
||||
[
|
||||
'route' => 'app_house_manager_index',
|
||||
'title' => 'Dashboard',
|
||||
'icon' => 'chart',
|
||||
],
|
||||
[
|
||||
'route' => 'app_house_manager_feedback_index',
|
||||
'title' => 'Feedback',
|
||||
'icon' => 'feedback',
|
||||
'hideChildren' => true,
|
||||
'children' => [
|
||||
[
|
||||
'route' => 'app_house_manager_feedback_provide',
|
||||
'title' => 'Feedback abgeben',
|
||||
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$menu = $this->createMenu($menuItems);
|
||||
|
||||
$this->addDivider($menu);
|
||||
|
||||
$this->addAdminItem($menu);
|
||||
|
||||
$this->addDivider($menu);
|
||||
|
||||
$this->addLogoutItem($menu);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
}
|
||||
@@ -109,4 +109,29 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function getPendingFeedbackQuery(array $hotelBusProIds = null): Query
|
||||
{
|
||||
$qb = $this->createQueryBuilder('disposition');
|
||||
|
||||
$qb
|
||||
->select('disposition', 'assignment', 'destination')
|
||||
->innerJoin('disposition.assignment', 'assignment')
|
||||
->innerJoin('assignment.destination', 'destination')
|
||||
->where($qb->expr()->andX(
|
||||
$qb->expr()->isNull('disposition.feedback'),
|
||||
$qb->expr()->lt('destination.dateTo', ':dateTo')
|
||||
))
|
||||
->setParameter('dateTo', new \DateTimeImmutable())
|
||||
;
|
||||
|
||||
if (null !== $hotelBusProIds) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->in('destination.hotelBusProId', ':hotelBusProIds'))
|
||||
->setParameter('hotelBusProIds', $hotelBusProIds)
|
||||
;
|
||||
}
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,29 +38,4 @@ class FeedbackRepository extends ServiceEntityRepository
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Feedback[] Returns an array of Feedback objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('f')
|
||||
// ->andWhere('f.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('f.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Feedback
|
||||
// {
|
||||
// return $this->createQueryBuilder('f')
|
||||
// ->andWhere('f.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
use App\BusProNet\DataProvider\HotelDataProvider;
|
||||
use App\BusProNet\Model\Hotel;
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
@@ -14,6 +16,11 @@ class DispositionVoter extends Voter
|
||||
public const DELETE = 'DELETE';
|
||||
public const CONTRACT = 'CONTRACT';
|
||||
public const INVOICE = 'INVOICE';
|
||||
public const FEEDBACK = 'FEEDBACK';
|
||||
|
||||
public function __construct(private readonly HotelDataProvider $hotelDataProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected function supports(string $attribute, mixed $subject): bool
|
||||
{
|
||||
@@ -26,18 +33,38 @@ class DispositionVoter extends Voter
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
/** @var Disposition $disposition */
|
||||
$disposition = $subject;
|
||||
/** @var User $user */
|
||||
$user = $token->getUser();
|
||||
|
||||
// Administrative users have full access to all dispositions
|
||||
if (in_array('ROLE_ADMINISTRATIVE', $token->getRoleNames())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// House managers may provide feedback for dispositions that are past
|
||||
// and are associated with their hotel
|
||||
if (in_array('ROLE_HOUSE_MANAGER', $token->getRoleNames()) && static::FEEDBACK === $attribute) {
|
||||
$hotels = $this
|
||||
->hotelDataProvider
|
||||
->findByCode($user->getHotelCode())
|
||||
;
|
||||
$hotelBusProIds = array_map(function (Hotel $hotel) {
|
||||
return $hotel->getBusProId();
|
||||
}, $hotels);
|
||||
$destination = $disposition
|
||||
->getAssignment()
|
||||
->getDestination()
|
||||
;
|
||||
|
||||
return in_array($destination->getHotelBusProId(), $hotelBusProIds)
|
||||
&& $destination->getDateTo() < new \DateTimeImmutable();
|
||||
}
|
||||
|
||||
// Teamers may only view or edit their own dispositions
|
||||
if (in_array('ROLE_TEAMER', $token->getRoleNames())) {
|
||||
/** @var User $user */
|
||||
$user = $token->getUser();
|
||||
$teamer = $user->getTeamer();
|
||||
/** @var Disposition $disposition */
|
||||
$disposition = $subject;
|
||||
|
||||
switch ($attribute) {
|
||||
case static::VIEW:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{% extends 'admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}Teamer:innenübersicht{% endblock %}
|
||||
{% block title %}Teamerübersicht{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Teamer:innenübersicht
|
||||
Teamerübersicht
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<button type="button"
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
{% extends 'house_manager/layout.html.twig' %}
|
||||
|
||||
{% block title %}Ausstehendes Feedback{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Ausstehendes Feedback
|
||||
</h1>
|
||||
</div>
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Name', 'teamer.lastName') }}
|
||||
</th>
|
||||
<th>
|
||||
Vorname
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Einsatz­zeitraum', 'destination.dateFrom') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Destination', 'destination.product') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Jobprofil', 'job_profile.name') }}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for disposition in pagination %}
|
||||
{% set teamer = disposition.teamer %}
|
||||
{% set assignment = disposition.assignment %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if teamer.photo %}
|
||||
<img src="{{ asset(teamer.photo.filename | imagine_filter('profile')) }}"
|
||||
class="w-12 h-auto rounded-full"
|
||||
alt="{{ teamer.firstName }}">
|
||||
{% else %}
|
||||
<div class="flex flex-col items-center justify-center w-12 h-12 border-2 border-gray-200 rounded-full">
|
||||
{{ icon('user', 'w-10 h-10 text-gray-200') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben">
|
||||
<span class="whitespace-nowrap">{{ teamer.lastName }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben">
|
||||
<span class="whitespace-nowrap">{{ teamer.firstName }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben">
|
||||
{{ assignment.effectivePeriod.start|date('d.m.Y') }} -
|
||||
<br>
|
||||
{{ assignment.effectivePeriod.end|date('d.m.Y') }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben"
|
||||
class="flex items-start space-x-2">
|
||||
{{ icon('flag-' ~ assignment.destination.country, 'w-5 h-5 shrink-0') }}
|
||||
<div class="flex-1">
|
||||
{{ assignment.destination.product }}
|
||||
<br>
|
||||
{{ assignment.destination.hotel }}
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben">
|
||||
{{ assignment.jobProfile.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center space-x-1 justify-end">
|
||||
<a href="{{ path('app_house_manager_feedback_provide', { 'uuid': disposition.uuid }) }}"
|
||||
title="Feedback abgeben">
|
||||
{{ icon('feedback') }}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<td colspan="7">
|
||||
Keine Daten...
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ knp_pagination_render(pagination) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,31 @@
|
||||
{% extends 'house_manager/layout.html.twig' %}
|
||||
|
||||
{% block title %}Feedback abgeben{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-4">
|
||||
Dein Feedback zu {{ disposition.teamer }}
|
||||
</h1>
|
||||
<div class="pb-8 text-lg">
|
||||
{{ disposition.assignment.jobProfile.name }}, {{ disposition.assignment.destination }}
|
||||
</div>
|
||||
{{ form_start(form) }}
|
||||
<div class="flex flex-col divide-y divide-gray-200">
|
||||
{% for child in form.ratings.children %}
|
||||
<div class="grid grid-cols-4 gap-x-4 items-start py-4">
|
||||
<h2 class="col-span-3 text-lg font-bold">
|
||||
{{ child.vars.label }}
|
||||
</h2>
|
||||
{{ form_widget(child) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="py-4">
|
||||
{{ form_row(form.comment) }}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends 'house_manager/layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block main_menu %}
|
||||
{{ knp_menu_render(knp_menu_get('house_manager_main')) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block mobile_menu %}
|
||||
{{ knp_menu_render(knp_menu_get('house_manager_main')) }}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user