feat: extend teamer dashboard
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?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 Version20231212155746 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 author VARCHAR(255) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE feedback DROP author');
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Controller\HouseManager\Feedback;
|
||||
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Feedback;
|
||||
use App\Entity\User;
|
||||
use App\Event\FeedbackProvidedEvent;
|
||||
use App\Form\FeedbackType;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -28,6 +29,8 @@ class ProvideController extends AbstractController
|
||||
#[IsGranted('FEEDBACK', subject: 'disposition')]
|
||||
public function index(Disposition $disposition, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
$assignment = $disposition->getAssignment();
|
||||
$destination = $assignment->getDestination();
|
||||
|
||||
@@ -42,7 +45,7 @@ class ProvideController extends AbstractController
|
||||
->getFeedbackSet()
|
||||
;
|
||||
|
||||
$feedback = new Feedback($assignment);
|
||||
$feedback = new Feedback($assignment, $user);
|
||||
|
||||
$form = $this->createForm(FeedbackType::class, $feedback, ['feedback_set' => $feedbackSet]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Teamer;
|
||||
|
||||
use App\Entity\Feedback;
|
||||
use App\Model\AjaxModalResponseDto;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class FeedbackController extends AbstractController
|
||||
{
|
||||
#[Route('/teamer/feedback/{uuid}', name: 'app_teamer_feedback')]
|
||||
#[IsGranted('VIEW', subject: 'feedback')]
|
||||
public function index(Feedback $feedback): JsonResponse
|
||||
{
|
||||
$response = new AjaxModalResponseDto();
|
||||
$response->setContent($this->renderView('teamer/feedback/index.html.twig', [
|
||||
'feedback' => $feedback,
|
||||
]));
|
||||
|
||||
return $this->json($response);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Controller\Teamer;
|
||||
|
||||
use App\Entity\Application;
|
||||
use App\Entity\Assignment;
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Feedback;
|
||||
use App\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -30,8 +33,29 @@ class IndexController extends AbstractController
|
||||
->getNewMatchingTeamerProfile($teamer)
|
||||
;
|
||||
|
||||
$recentFeedback = $this
|
||||
->entityManager
|
||||
->getRepository(Feedback::class)
|
||||
->getRecentForTeamer($teamer)
|
||||
;
|
||||
|
||||
$pendingApplications = $this
|
||||
->entityManager
|
||||
->getRepository(Application::class)
|
||||
->getPendingForTeamer($teamer)
|
||||
;
|
||||
|
||||
$upcomingDispositions = $this
|
||||
->entityManager
|
||||
->getRepository(Disposition::class)
|
||||
->getUpcomingDispositionsByTeamer($teamer)
|
||||
;
|
||||
|
||||
return $this->render('teamer/index.html.twig', [
|
||||
'matchingAssignments' => $matchingAssignments,
|
||||
'recentFeedback' => $recentFeedback,
|
||||
'pendingApplications' => $pendingApplications,
|
||||
'upcomingDispositions' => $upcomingDispositions,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -56,12 +56,16 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
private ?string $commentInternal = null;
|
||||
|
||||
public function __construct(Assignment $assignment)
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $author = null;
|
||||
|
||||
public function __construct(Assignment $assignment, User $author)
|
||||
{
|
||||
$this->uuid = Uuid::v4();
|
||||
$this->destinationName = $assignment->getDestination()->getProduct();
|
||||
$this->assignmentDate = $assignment->getEffectiveDateFrom();
|
||||
$this->jobProfileName = $assignment->getJobProfile()->getName();
|
||||
$this->author = $author->getFullName();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
@@ -207,4 +211,16 @@ class Feedback implements BlameableEntityInterface, TimestampableEntityInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthor(): ?string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function setAuthor(string $author): static
|
||||
{
|
||||
$this->author = $author;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,15 @@ class ApplicationRepository extends ServiceEntityRepository
|
||||
;
|
||||
}
|
||||
|
||||
public function getPendingForTeamer(Teamer $teamer, int $limit = 5): array
|
||||
{
|
||||
return $this
|
||||
->getPendingForTeamerQuery($teamer)
|
||||
->setMaxResults($limit)
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function getCurrentByAssignment(Assignment $assignment): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('application');
|
||||
|
||||
@@ -221,10 +221,12 @@ class AssignmentRepository extends ServiceEntityRepository
|
||||
->select('assignment', 'destination', 'job_profile')
|
||||
->innerJoin('assignment.destination', 'destination')
|
||||
->innerJoin('assignment.jobProfile', 'job_profile')
|
||||
->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->neq('application.teamer', ':teamer'))
|
||||
->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->eq('application.teamer', ':teamer'))
|
||||
->leftJoin('assignment.dispositions', 'disposition', Join::WITH, $qb->expr()->eq('disposition.teamer', ':teamer'))
|
||||
->where($qb->expr()->andX(
|
||||
$qb->expr()->in('assignment.jobProfile', ':jobProfiles'),
|
||||
$qb->expr()->isNull('application')
|
||||
$qb->expr()->isNull('application'),
|
||||
$qb->expr()->isNull('disposition')
|
||||
))
|
||||
->setParameter('jobProfiles', $teamer->getJobProfiles())
|
||||
->setParameter('teamer', $teamer)
|
||||
|
||||
@@ -29,8 +29,9 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
$qb = $this->createQueryBuilder('disposition');
|
||||
|
||||
return $qb
|
||||
->select('disposition', 'assignment', 'job_profile', 'destination')
|
||||
->select('disposition', 'assignment', 'job_profile', 'destination', 'documents')
|
||||
->innerJoin('disposition.assignment', 'assignment')
|
||||
->leftJoin('assignment.documents', 'documents')
|
||||
->innerJoin('assignment.jobProfile', 'job_profile')
|
||||
->innerJoin('assignment.destination', 'destination')
|
||||
->where($qb->expr()->andX(
|
||||
@@ -52,6 +53,15 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
;
|
||||
}
|
||||
|
||||
public function getUpcomingDispositionsByTeamer(Teamer $teamer, int $limit = 5): array
|
||||
{
|
||||
return $this
|
||||
->getUpcomingDispositionsByTeamerQuery($teamer)
|
||||
->setMaxResults($limit)
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function getRecentDispositionsByTeamerQuery(Teamer $teamer): Query
|
||||
{
|
||||
$qb = $this->createQueryBuilder('disposition');
|
||||
@@ -123,10 +133,12 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
->innerJoin('assignment.destination', 'destination')
|
||||
->where($qb->expr()->andX(
|
||||
$qb->expr()->in('destination.hotelBusProId', ':hotelBusProIds'),
|
||||
$qb->expr()->lt('destination.dateTo', ':dateTo')
|
||||
$qb->expr()->lt('destination.dateTo', ':dateTo'),
|
||||
$qb->expr()->eq('disposition.status', ':status')
|
||||
))
|
||||
->setParameter('hotelBusProIds', $hotelBusProIds)
|
||||
->setParameter('dateTo', new \DateTimeImmutable())
|
||||
->setParameter('status', Disposition::STATUS_NEW)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Feedback;
|
||||
use App\Entity\Teamer;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
@@ -28,6 +29,8 @@ class FeedbackRepository extends ServiceEntityRepository
|
||||
return $qb
|
||||
->select('feedback', 'teamer')
|
||||
->innerJoin('feedback.teamer', 'teamer')
|
||||
->where($qb->expr()->eq('feedback.status', ':status'))
|
||||
->setParameter('status', Feedback::STATUS_NEW)
|
||||
->orderBy('feedback.createdAt', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->getQuery()
|
||||
@@ -47,4 +50,22 @@ class FeedbackRepository extends ServiceEntityRepository
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function getRecentForTeamer(Teamer $teamer, int $limit = 5): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('feedback');
|
||||
|
||||
return $qb
|
||||
->where($qb->expr()->andX(
|
||||
$qb->expr()->eq('feedback.teamer', ':teamer'),
|
||||
$qb->expr()->eq('feedback.status', ':status')
|
||||
))
|
||||
->setParameter('teamer', $teamer)
|
||||
->setParameter('status', Feedback::STATUS_PUBLISHED)
|
||||
->orderBy('feedback.createdAt', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ class AppExtension extends AbstractExtension
|
||||
new TwigFilter('file_icon', [AppRuntime::class, 'fileIconFilter'], ['is_safe' => ['html']]),
|
||||
new TwigFilter('date_diff', [AppRuntime::class, 'dateDiffForHumans']),
|
||||
new TwigFilter('format_money', [AppRuntime::class, 'formatMoney']),
|
||||
new TwigFilter('format_rating', [AppRuntime::class, 'formatRating'], ['is_safe' => ['html']]),
|
||||
new TwigFilter('license_label', [AppRuntime::class, 'licenseLabel']),
|
||||
new TwigFilter('teamer_status_label', [AppRuntime::class, 'teamerStatusLabel']),
|
||||
new TwigFilter('bpn_country_label', [AppRuntime::class, 'bpnCountryLabel']),
|
||||
|
||||
@@ -89,6 +89,14 @@ class AppRuntime implements RuntimeExtensionInterface
|
||||
return $this->intlExtension->formatCurrency($amount, 'EUR');
|
||||
}
|
||||
|
||||
public function formatRating(int $rating): string
|
||||
{
|
||||
// Ratings are stored as integers so divide by 100 first
|
||||
$rating = $rating / 100;
|
||||
|
||||
return 'Ø '.$this->intlExtension->formatNumber($rating, ['fraction_digit' => 2]);
|
||||
}
|
||||
|
||||
public function renderIcon(Environment $environment, string $icon, string $classes = 'w-5 h-5'): string
|
||||
{
|
||||
return $environment->render('_partials/_icon.html.twig', [
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
<div class="lg:col-span-3">
|
||||
{{ form_row(form.destination) }}
|
||||
</div>
|
||||
{{ form_row(form.status) }}
|
||||
{{ form_row(form.availableDispositions) }}
|
||||
<div>
|
||||
<h4 class="font-bold pb-2">
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
{% block content %}
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Neue Bewerbungen
|
||||
</h2>
|
||||
{% if newApplicationsCount > 0 or pendingApplicationsCount > 0 %}
|
||||
<h3 class="font-bold pb-2">
|
||||
{{ newApplicationsCount }} neu, {{ pendingApplicationsCount }} in Bearbeitung
|
||||
</h3>
|
||||
{% endif %}
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for application in applications %}
|
||||
{% set assignment = application.assignment %}
|
||||
@@ -32,12 +34,14 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Neue Dokumente
|
||||
</h2>
|
||||
{% if newDocumentsCount > 0 or pendingDocumentsCount > 0 %}
|
||||
<h3 class="font-bold pb-2">
|
||||
{{ newDocumentsCount }} neu {{ pendingDocumentsCount }} in Bearbeitung
|
||||
</h3>
|
||||
{% endif %}
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for document in documents %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
Gesamtnote
|
||||
</th>
|
||||
<th>
|
||||
Kommentar
|
||||
Kommentar öffentlich/intern
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -40,25 +39,25 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if disposition.feedback %}
|
||||
{{ disposition.feedback.averageRating }}
|
||||
{{ (disposition.feedback.averageRating/100)|format_number({fraction_digit: 2}) }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if disposition.feedback %}
|
||||
<div class="truncate">
|
||||
<div class="divide-y">
|
||||
<div class="pb-2">
|
||||
{{ disposition.feedback.comment|default('-') }}
|
||||
</div>
|
||||
<div class="pt-2">
|
||||
{{ disposition.feedback.commentInternal|default('-') }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button>
|
||||
{{ icon('feedback') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<td colspan="7">
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<h2 class=" font-bold text-lg pb-2">
|
||||
Durchschnittsnote: {{ feedback.averageRating|format_rating }}
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200 pb-4">
|
||||
{% for item in feedback.ratings %}
|
||||
<li class="py-2 first:pt-0 last:pb-0 flex items-start">
|
||||
<span class="flex-1">{{ item.rating }}</span>
|
||||
<strong>Note {{ item.mark }}</strong>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>
|
||||
abgegeben von {{ feedback.author }} (<a href="mailto:{{ feedback.createdBy }}" class="underline">{{ feedback.createdBy }}</a>)
|
||||
</p>
|
||||
@@ -10,39 +10,127 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Meine Bewerbungen
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for application in pendingApplications %}
|
||||
{% set assignment = application.assignment %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
<a href="{{ path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) }}" class="flex items-center space-x-1 truncate">
|
||||
{{ icon('hand', 'w-4 h-4 shrink-0') }}
|
||||
<span class="whitespace-nowrap">{{ assignment.effectivePeriod.start|date('d.m.y') }} - {{ assignment.effectivePeriod.end|date('d.m.y') }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.destination.hotel }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.jobProfile.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
-
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Meine Dokumente
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for disposition in upcomingDispositions %}
|
||||
{% for document in disposition.assignment.documents %}
|
||||
{% if is_granted('DOWNLOAD', document) %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
<a href="{{ path('app_common_download', { 'uuid': document.uuid }) }}" class="flex items-center space-x-1 truncate" target="_blank">
|
||||
{{ icon('download', 'w-4 h-4 shrink-0') }}
|
||||
<span class="whitespace-nowrap">{{ document.displayName|default(document.originalFilename) }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
-
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Mein Feedback
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for feedback in recentFeedback %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
<button type="button" class="flex items-center space-x-1 truncate"
|
||||
{{ stimulus_controller('modal-button', [], [], {'ajax-modal': '#ajax-modal'}) }}
|
||||
{{ stimulus_action('modal-button', 'ajax', null, {
|
||||
'title': feedback.destinationName ~ ' ' ~ feedback.assignmentDate|date('m.Y'),
|
||||
'url': path('app_teamer_feedback', { 'uuid': feedback.uuid })
|
||||
}) }}>
|
||||
{{ icon('feedback', 'w-4 h-4 shrink-0') }}
|
||||
<span class="whitespace-nowrap">{{ feedback.createdAt|date('d.m.y') }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ feedback.author }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ feedback.averageRating|format_rating }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span>{{ feedback.comment }}</span>
|
||||
</button>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
-
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Meine Einsätze
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for disposition in upcomingDispositions %}
|
||||
{% set assignment = disposition.assignment %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
<a href="{{ path('app_teamer_disposition_detail', { 'uuid': disposition.uuid }) }}" class="flex items-center space-x-1 truncate">
|
||||
{{ icon('hand', 'w-4 h-4 shrink-0') }}
|
||||
<span class="whitespace-nowrap">{{ assignment.effectivePeriod.start|date('d.m.y') }} - {{ assignment.effectivePeriod.end|date('d.m.y') }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.destination.hotel }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.jobProfile.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
-
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 border border-gray-200 rounded-md px-4 py-2">
|
||||
<h2 class="font-bold text-lg">
|
||||
<h2 class="font-bold text-lg pb-2">
|
||||
Mögliche Einsätze
|
||||
</h2>
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{{ matchingAssignments|length }}
|
||||
{% for assignment in matchingAssignments %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
<a href="{{ path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) }}" class="flex items-center space-x-1 truncate">
|
||||
{{ icon('calendar', 'w-4 h-4 shrink-0') }}
|
||||
<span class="whitespace-nowrap">{{ assignment.effectivePeriod.start|date('d.m.y') }} - {{ assignment.effectivePeriod.end|date('d.m.y') }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.destination.hotel }}</span>
|
||||
{% include '_partials/_dot.html.twig' %}
|
||||
<span class="whitespace-nowrap">{{ assignment.jobProfile.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-2 first:pt-0 last:pb-0">
|
||||
-
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user