Feat: Add popup window to display teamer info per application
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Admin\Application;
|
||||||
|
|
||||||
|
use App\Entity\Application;
|
||||||
|
use App\Model\AjaxModalResponseDto;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class InfoController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/admin/application/info/{uuid}', name: 'app_admin_application_info')]
|
||||||
|
public function index(Application $application): JsonResponse
|
||||||
|
{
|
||||||
|
$response = new AjaxModalResponseDto();
|
||||||
|
$response->setContent($this->renderView('admin/application/info.html.twig', [
|
||||||
|
'application' => $application,
|
||||||
|
]));
|
||||||
|
|
||||||
|
return $this->json($response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Admin\Teamer;
|
||||||
|
|
||||||
|
use App\Entity\Teamer;
|
||||||
|
use App\Model\AjaxModalResponseDto;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class InfoController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/admin/teamer/info/{uuid}', name: 'app_admin_teamer_info')]
|
||||||
|
public function index(Teamer $teamer): JsonResponse
|
||||||
|
{
|
||||||
|
$response = new AjaxModalResponseDto();
|
||||||
|
|
||||||
|
return $this->json($response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ namespace App\Controller\Common;
|
|||||||
use App\Entity\Upload;
|
use App\Entity\Upload;
|
||||||
use App\Service\Upload\UploadHandler;
|
use App\Service\Upload\UploadHandler;
|
||||||
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\HttpFoundation\ResponseHeaderBag;
|
||||||
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;
|
||||||
|
|
||||||
@@ -17,11 +19,13 @@ class DownloadController extends AbstractController
|
|||||||
|
|
||||||
#[Route('/download/{uuid}', name: 'app_common_download')]
|
#[Route('/download/{uuid}', name: 'app_common_download')]
|
||||||
#[IsGranted('DOWNLOAD', subject: 'upload')]
|
#[IsGranted('DOWNLOAD', subject: 'upload')]
|
||||||
public function index(Upload $upload): Response
|
public function index(Upload $upload, Request $request): Response
|
||||||
{
|
{
|
||||||
$path = $this->uploadHandler->getUploadFilepath($upload);
|
$path = $this->uploadHandler->getUploadFilepath($upload);
|
||||||
$originalFilename = $upload->getOriginalFilename();
|
$originalFilename = $upload->getOriginalFilename();
|
||||||
|
$inline = (bool) $request->get('inline');
|
||||||
|
$disposition = $inline ? ResponseHeaderBag::DISPOSITION_INLINE : ResponseHeaderBag::DISPOSITION_ATTACHMENT;
|
||||||
|
|
||||||
return $this->file($path, $originalFilename);
|
return $this->file($path, $originalFilename, $disposition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ class Application implements TimestampableEntityInterface
|
|||||||
return $teamer->hasPickup($pickupId);
|
return $teamer->hasPickup($pickupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCompletedTrainingsCount(): int
|
public function matchesRequiredTrainings(): bool
|
||||||
{
|
{
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$requiredTrainings = $this->getAssignment()->getJobProfile()->getRequiredTrainings();
|
$requiredTrainings = $this->getAssignment()->getJobProfile()->getRequiredTrainings();
|
||||||
@@ -141,6 +141,6 @@ class Application implements TimestampableEntityInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $count;
|
return $count === $requiredTrainings->count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<ul class="list-disc pl-4 pb-4">
|
<ul class="pb-8 divide-y divide-gray-200">
|
||||||
{% if assignment.pickup and assignment.effectivePickupDate %}
|
{% if assignment.pickup and assignment.effectivePickupDate %}
|
||||||
<li>
|
<li class="py-2">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center justify-between">
|
||||||
<span>Busbegleitung ab {{ assignment.pickup|bpn_pickup_label }} am {{ assignment.effectivePickupDate|date('d.m.Y') }}</span>
|
<span class="flex-1">Busbegleitung {{ assignment.pickup|bpn_pickup_label }} am {{ assignment.effectivePickupDate|date('d.m.Y') }}</span>
|
||||||
{% if teamer.hasPickup(assignment.pickup) %}
|
{% if teamer.hasPickup(assignment.pickup) %}
|
||||||
{{ icon('check', 'w-5 h-5 text-emerald-500') }}
|
{{ icon('check', 'w-5 h-5 text-green-500 shrink-0') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ icon('close', 'w-5 h-5 text-red-500') }}
|
{{ icon('close', 'w-5 h-5 text-red-500 shrink-0') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for training in assignment.jobProfile.requiredTrainings %}
|
{% for training in assignment.jobProfile.requiredTrainings %}
|
||||||
<li>
|
<li class="py-2">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center justify-between">
|
||||||
<span>{{ training.name }} absolviert</span>
|
<span class="flex-1">{{ training.name }} absolviert</span>
|
||||||
{% if teamer.trainingAttendance(training) %}
|
{% if teamer.trainingAttendance(training) %}
|
||||||
{{ icon('check', 'w-5 h-5 text-emerald-500') }}
|
{{ icon('check', 'w-5 h-5 text-green-500 shrink-0') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ icon('close', 'w-5 h-5 text-red-500') }}
|
{{ icon('close', 'w-5 h-5 text-red-500 shrink-0') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -64,7 +64,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="flex items-center justify-end space-x-1">
|
<div class="flex items-center justify-end space-x-1"
|
||||||
|
role="button"
|
||||||
|
title="Bewerber:innen-Info anzeigen"
|
||||||
|
aria-label="Bewerber:innen-Info anzeigen"
|
||||||
|
{{ stimulus_controller('modal-button', [], [], {'ajax-modal': '#ajax-modal'}) }}
|
||||||
|
{{ stimulus_action('modal-button', 'ajax', 'click', {
|
||||||
|
'title': 'Bewerber:innen-Info',
|
||||||
|
'url': path('app_admin_application_info', { 'uuid': application.uuid })
|
||||||
|
}) }}>
|
||||||
{% if assignment.pickup %}
|
{% if assignment.pickup %}
|
||||||
{% if application.matchesPickup %}
|
{% if application.matchesPickup %}
|
||||||
{{ icon('bus', 'w-5 h-5 text-green-500 shrink-0') }}
|
{{ icon('bus', 'w-5 h-5 text-green-500 shrink-0') }}
|
||||||
@@ -73,8 +81,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set requiredTrainingsCount = assignment.jobProfile.requiredTrainings|length %}
|
{% set requiredTrainingsCount = assignment.jobProfile.requiredTrainings|length %}
|
||||||
{% if requiredTrainingsCount > 0 %}
|
{% if not application.matchesRequiredTrainings %}
|
||||||
<span>{{ application.completedTrainingsCount }}/{{ requiredTrainingsCount }}</span>
|
{{ icon('alert', 'w-5 h-5 text-red-500 shrink-0') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<h3 class="text-xl font-bold pb-4">
|
||||||
|
{{ application.teamer }}
|
||||||
|
</h3>
|
||||||
|
{% include '_partials/_assignment_requirements_info.html.twig' with {
|
||||||
|
'assignment': application.assignment,
|
||||||
|
'teamer': application.teamer
|
||||||
|
} %}
|
||||||
|
<ul class="pb-8 divide-y divide-gray-200">
|
||||||
|
{% for type in ['ski', 'snowboard'] %}
|
||||||
|
<li class="py-2">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="flex-1">{{ type|license_label }}</span>
|
||||||
|
{% set license = application.teamer.licenseOfType(type) %}
|
||||||
|
{% if license %}
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<span>{{ license.date|date('m/Y') }}</span>
|
||||||
|
{% if is_granted('DOWNLOAD', license.certificate) %}
|
||||||
|
<a href="{{ path('app_common_download', { 'uuid': license.certificate.uuid, 'inline': true }) }}"
|
||||||
|
target="_blank"
|
||||||
|
title="Download Nachweis">
|
||||||
|
{{ icon('download', 'w-4 h-4') }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{{ icon('minus', 'w-4 h-4') }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
@@ -71,7 +71,8 @@
|
|||||||
{{ icon('edit') }}
|
{{ icon('edit') }}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ path('app_common_download', { 'uuid': upload.uuid }) }}"
|
<a href="{{ path('app_common_download', { 'uuid': upload.uuid, 'inline': true }) }}"
|
||||||
|
target="_blank"
|
||||||
title="Download">
|
title="Download">
|
||||||
{{ icon('download') }}
|
{{ icon('download') }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user