diff --git a/src/Controller/Teamer/AssignmentController.php b/src/Controller/Teamer/Assignment/DetailController.php similarity index 90% rename from src/Controller/Teamer/AssignmentController.php rename to src/Controller/Teamer/Assignment/DetailController.php index ea83bc1..a3c76fd 100644 --- a/src/Controller/Teamer/AssignmentController.php +++ b/src/Controller/Teamer/Assignment/DetailController.php @@ -1,6 +1,6 @@ getBookmarks()->contains($assignment); - return $this->render('teamer/assignment/index.html.twig', [ + return $this->render('teamer/assignment/detail.html.twig', [ 'teamer' => $teamer, 'assignment' => $assignment, 'disposition' => $disposition, diff --git a/src/Controller/Teamer/Assignment/IndexController.php b/src/Controller/Teamer/Assignment/IndexController.php new file mode 100644 index 0000000..1ef2112 --- /dev/null +++ b/src/Controller/Teamer/Assignment/IndexController.php @@ -0,0 +1,61 @@ +getUser(); + $teamer = $user->getTeamer(); + + // Validate teamer data to show missing data right away + $errors = $this->validator->validate($teamer, null, ['profile_preflight']); + + $filterDto = $this->filterHandler->getFilterSettings(); + + $query = $this + ->assignmentRepository + ->getListQueryForTeamer($teamer, $filterDto) + ; + + $pagination = $this->paginator->paginate( + $query, + $request->query->getInt('page', 1), + 10, + [ + 'defaultSortFieldName' => 'destination.dateFrom', + 'defaultSortDirection' => 'asc', + ] + ); + + return $this->render('teamer/assignment/index.html.twig', [ + 'teamer' => $teamer, + 'errors' => $errors, + 'pagination' => $pagination, + 'filterDto' => $filterDto, + ]); + } +} \ No newline at end of file diff --git a/src/Controller/Teamer/IndexController.php b/src/Controller/Teamer/IndexController.php index 3ba05ef..af869e5 100644 --- a/src/Controller/Teamer/IndexController.php +++ b/src/Controller/Teamer/IndexController.php @@ -2,60 +2,37 @@ namespace App\Controller\Teamer; +use App\Entity\Assignment; use App\Entity\User; -use App\Repository\AssignmentRepository; -use App\Service\Common\AssignmentFilterHandler; -use Knp\Component\Pager\PaginatorInterface; +use Doctrine\ORM\EntityManagerInterface; 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\Component\Validator\Validator\ValidatorInterface; class IndexController extends AbstractController { - public function __construct( - private readonly ValidatorInterface $validator, - private readonly AssignmentFilterHandler $filterHandler, - private readonly PaginatorInterface $paginator, - private readonly AssignmentRepository $assignmentRepository - ) { + public function __construct(private readonly EntityManagerInterface $entityManager) + { } #[Route('/teamer', name: 'app_teamer_index')] #[IsGranted('ROLE_TEAMER')] - public function index(Request $request): Response + public function index(): Response { /** @var User $user */ $user = $this->getUser(); $teamer = $user->getTeamer(); + $availabilities = $teamer->getAllAvailabilities()->toArray(); - // Validate teamer data to show missing data right away - $errors = $this->validator->validate($teamer, null, ['profile_preflight']); - - $filterDto = $this->filterHandler->getFilterSettings(); - - $query = $this - ->assignmentRepository - ->getListQueryForTeamer($teamer, $filterDto) + $matchingAssignments = $this + ->entityManager + ->getRepository(Assignment::class) + ->getNewMatchingTeamerProfile($teamer, $availabilities) ; - $pagination = $this->paginator->paginate( - $query, - $request->query->getInt('page', 1), - 10, - [ - 'defaultSortFieldName' => 'destination.dateFrom', - 'defaultSortDirection' => 'asc', - ] - ); - return $this->render('teamer/index.html.twig', [ - 'teamer' => $teamer, - 'errors' => $errors, - 'pagination' => $pagination, - 'filterDto' => $filterDto, + 'matchingAssignments' => $matchingAssignments, ]); } } \ No newline at end of file diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php index a36172b..0aacdbb 100644 --- a/src/Entity/Teamer.php +++ b/src/Entity/Teamer.php @@ -103,7 +103,7 @@ class Teamer implements TimestampableEntityInterface #[ORM\OneToMany(mappedBy: 'teamer', targetEntity: License::class, cascade: ['remove'])] private Collection $licenses; - #[ORM\ManyToMany(targetEntity: JobProfile::class, orphanRemoval: true)] + #[ORM\ManyToMany(targetEntity: JobProfile::class)] private Collection $jobProfiles; #[ORM\ManyToMany(targetEntity: Assignment::class, inversedBy: 'teamers')] @@ -439,6 +439,21 @@ class Teamer implements TimestampableEntityInterface return $this; } + public function getAllAvailabilities(): Collection + { + $allAvailabilities = new ArrayCollection(); + + foreach ($this->getAvailabilities() as $availability) { + $allAvailabilities->add($availability); + } + + foreach ($this->getCustomAvailabilities() as $availability) { + $allAvailabilities->add($availability); + } + + return $allAvailabilities; + } + public function getPhoto(): ?Upload { return $this->photo; diff --git a/src/Menu/TeamerMenuBuilder.php b/src/Menu/TeamerMenuBuilder.php index 20200ce..6e4ba02 100644 --- a/src/Menu/TeamerMenuBuilder.php +++ b/src/Menu/TeamerMenuBuilder.php @@ -11,12 +11,17 @@ class TeamerMenuBuilder extends AbstractMenuBuilder $menuItems = [ [ 'route' => 'app_teamer_index', + 'title' => 'Dashboard', + 'icon' => 'chart', + ], + [ + 'route' => 'app_teamer_assignment_index', 'title' => 'Einsatzübersicht', 'icon' => 'calendar', 'hideChildren' => true, 'children' => [ [ - 'route' => 'app_teamer_assignment', + 'route' => 'app_teamer_assignment_detail', 'title' => 'Einsatzdetails', 'routeParameters' => $this->getDefaultRouteParameters('uuid'), ] @@ -51,7 +56,7 @@ class TeamerMenuBuilder extends AbstractMenuBuilder 'title' => 'Merkliste', 'children' => [ [ - 'route' => 'app_teamer_assignment', + 'route' => 'app_teamer_assignment_detail', 'title' => 'Einsatzdetails', 'routeParameters' => $this->getDefaultRouteParameters('uuid'), ] diff --git a/src/Repository/AssignmentRepository.php b/src/Repository/AssignmentRepository.php index 55fb73b..3a78188 100644 --- a/src/Repository/AssignmentRepository.php +++ b/src/Repository/AssignmentRepository.php @@ -202,4 +202,36 @@ class AssignmentRepository extends ServiceEntityRepository ->getResult() ; } + + public function getNewMatchingTeamerProfile(Teamer $teamer, array $availabilities, int $limit = 5): array + { + $qb = $this->createQueryBuilder('assignment'); + + $qb + ->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')) + ->where($qb->expr()->andX( + $qb->expr()->in('assignment.jobProfile', ':jobProfiles'), + $qb->expr()->isNull('application') + )) + ->setParameter('jobProfiles', $teamer->getJobProfiles()) + ->setParameter('teamer', $teamer) + ; + + foreach ($availabilities as $availability) { + $qb->orWhere($qb->expr()->andX( + $qb->expr()->eq('assignment.dateFrom', ':dateFrom'), + $qb->expr()->eq('assignment.dateTo', ':dateTo') + )); + } + + return $qb + ->orderBy('assignment.createdAt', 'DESC') + ->setMaxResults($limit) + ->getQuery() + ->getResult() + ; + } } diff --git a/templates/admin/index.html.twig b/templates/admin/index.html.twig index 0ea5d2a..f5d4644 100644 --- a/templates/admin/index.html.twig +++ b/templates/admin/index.html.twig @@ -111,5 +111,4 @@ - {% endblock %} \ No newline at end of file diff --git a/templates/teamer/application/index.html.twig b/templates/teamer/application/index.html.twig index 5d62a75..f5da450 100644 --- a/templates/teamer/application/index.html.twig +++ b/templates/teamer/application/index.html.twig @@ -38,12 +38,12 @@ {% if application.status == constant('App\\Entity\\Application::STATUS_REJECTED') %} {% set icon = 'close' %} {% set class = 'bg-gray-100 text-gray-800 hover:bg-gray-50' %} - {% set url = path('app_teamer_assignment', { 'uuid': assignment.uuid, 'r': return_url() }) %} + {% set url = path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) %} {% set label = 'Bewerbung abgelehnt' %} {% else %} {% set icon = 'hourglass' %} {% set class = 'bg-yellow-100 text-yellow-800 hover:bg-yellow-50' %} - {% set url = path('app_teamer_assignment', { 'uuid': assignment.uuid, 'r': return_url() }) %} + {% set url = path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) %} {% set label = 'in Bearbeitung' %} {% endif %} + Einsatzübersicht + +
+ {% include '_partials/_assignment_info.html.twig' %} +
+

+ Voraussetzungen für diesen Einsatz +

+ {% include '_partials/_assignment_requirements_info.html.twig' %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/teamer/assignment/index.html.twig b/templates/teamer/assignment/index.html.twig index 22598c9..85c9c28 100644 --- a/templates/teamer/assignment/index.html.twig +++ b/templates/teamer/assignment/index.html.twig @@ -3,67 +3,101 @@ {% block title %}Einsatzübersicht{% endblock %} {% block content %} -

- Einsatzübersicht -

-
- {% include '_partials/_assignment_info.html.twig' %} -
-

- Voraussetzungen für diesen Einsatz -

- {% include '_partials/_assignment_requirements_info.html.twig' %} -
- {% if disposition is not null %} - {% include '_partials/_infobox.html.twig' with { - 'message': 'Du wurdest am ' ~ disposition.createdAt|date('d.m.Y') ~ 'für diesen Einsatz eingeteilt.' - } %} - {% elseif application is not null %} - {% if application.status == constant('App\\Entity\\Application::STATUS_REJECTED') %} - {% include '_partials/_errorbox.html.twig' with { - 'message': 'Deine Bewerbung wurde leider abgelehnt.' - } %} - {% if application.comment and application.commentVisible %} - Begründung: {{ application.comment|nl2br}} - {% endif %} - {% elseif application.status == constant('App\\Entity\\Application::STATUS_PENDING') %} - {% include '_partials/_infobox.html.twig' with { - 'message': 'Deine Bewerbung befindet sich in Bearbeitung.' - } %} - {% if application.comment and application.commentVisible %} - Begründung: {{ application.comment|nl2br}} - {% endif %} +
+

+ Einsatz­übersicht +

+
+ + Filter {% endif %} - {% else %} -
- Erfüllst du mehrere oder alle Voraussetzungen für diesen Einsatz? -
- - Hier bewerben! - - - {% if not isBookmarked %}auf die Merkliste setzen{% else %}von der Merkliste löschen{% endif %} - - {% endif %} - - zurück +
+ + {% if filterDto.active %} + + Reset -
+ {% endif %}
+ + {{ knp_pagination_render(pagination, 'paginator/sliding_boxes.html.twig') }} +
+ {% for assignment in pagination %} +
+
+
+ {{ icon('flag-' ~ assignment.destination.country, 'w-5 h-5 shrink-0') }} + {{ assignment.destination.product }} +
+
+ {{ icon('calendar', 'w-5 h-5 shrink-0') }} + {{ assignment.effectivePeriod.start|date('d.m.Y') }} - {{ assignment.effectivePeriod.end|date('d.m.Y') }} +
+
+ {{ icon('profile', 'w-5 h-5 shrink-0') }} + {{ assignment.jobProfile.name }} +
+
+ {{ icon('house', 'w-5 h-5 shrink-0') }} + {{ assignment.destination.hotel }} +
+ {% if assignment.pickup %} +
+ {{ icon('bus', 'w-5 h-5 shrink-0') }} + {{ assignment.pickup|bpn_pickup_label|default('-') }} {{ assignment.effectivePickupDate ? assignment.effectivePickupDate|date('d.m.Y') : '' }} +
+ {% endif %} +
+
+ {% if assignment.applications|length %} + {% set application = assignment.applications[0] %} + {% if application.status == constant('App\\Entity\\Application::STATUS_REJECTED') %} + + {{ icon('close', 'w-4 h-4 shrink-0') }} + Bewerbung abgelehnt + + {% else %} + + {{ icon('hourglass', 'w-4 h-4 shrink-0') }} + in Bearbeitung + + {% endif %} + {% elseif assignment.dispositions|length %} + + {{ icon('check', 'w-4 h-4 shrink-0') }} + eingeteilt + + {% elseif is_granted('APPLY', assignment) %} + + {{ icon('hand', 'w-4 h-4 shrink-0') }} + bewerben + + {% endif %} +
+ {% set isBookmarked = teamer.bookmarks.contains(assignment) %} + + {{ icon('star', html_classes('w-5 h-5', { 'text-yellow-500': isBookmarked, 'text-gray-200': not isBookmarked } )) }} + +
+ {% endfor %} +
+ {{ knp_pagination_render(pagination, 'paginator/sliding_boxes.html.twig') }} {% endblock %} \ No newline at end of file diff --git a/templates/teamer/bookmark/index.html.twig b/templates/teamer/bookmark/index.html.twig index 9833eed..f59d191 100644 --- a/templates/teamer/bookmark/index.html.twig +++ b/templates/teamer/bookmark/index.html.twig @@ -37,7 +37,7 @@ {% if assignment.applications|length %} {% set icon = 'hourglass' %} {% set class = 'bg-yellow-100 text-yellow-800 hover:bg-yellow-50' %} - {% set url = path('app_teamer_assignment', { 'uuid': assignment.uuid, 'r': return_url() }) %} + {% set url = path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) %} {% set label = 'in Bearbeitung' %} {% elseif assignment.dispositions|length %} {% set icon = 'check' %} @@ -47,7 +47,7 @@ {% else %} {% set icon = 'hand' %} {% set class = 'btn--info' %} - {% set url = path('app_teamer_assignment', { 'uuid': assignment.uuid, 'r': return_url() }) %} + {% set url = path('app_teamer_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) %} {% set label = 'bewerben' %} {% endif %}

- Einsatz­übersicht + Dashboard

-
- - {% if filterDto.active %} - - Reset - - {% endif %} +
+
+
+

+ Meine Bewerbungen +

+
    +
+
+
+

+ Meine Dokumente +

+
    +
+
+
+

+ Mein Feedback +

+
    +
+
+
+

+ Meine Einsätze +

+
    +
+
+
+

+ Mögliche Einsätze +

+
    + {{ matchingAssignments|length }} +
- {{ knp_pagination_render(pagination, 'paginator/sliding_boxes.html.twig') }} -
- {% for assignment in pagination %} -
-
-
- {{ icon('flag-' ~ assignment.destination.country, 'w-5 h-5 shrink-0') }} - {{ assignment.destination.product }} -
-
- {{ icon('calendar', 'w-5 h-5 shrink-0') }} - {{ assignment.effectivePeriod.start|date('d.m.Y') }} - {{ assignment.effectivePeriod.end|date('d.m.Y') }} -
-
- {{ icon('profile', 'w-5 h-5 shrink-0') }} - {{ assignment.jobProfile.name }} -
-
- {{ icon('house', 'w-5 h-5 shrink-0') }} - {{ assignment.destination.hotel }} -
- {% if assignment.pickup %} -
- {{ icon('bus', 'w-5 h-5 shrink-0') }} - {{ assignment.pickup|bpn_pickup_label|default('-') }} {{ assignment.effectivePickupDate ? assignment.effectivePickupDate|date('d.m.Y') : '' }} -
- {% endif %} -
-
- {% if assignment.applications|length %} - {% set application = assignment.applications[0] %} - {% if application.status == constant('App\\Entity\\Application::STATUS_REJECTED') %} - - {{ icon('close', 'w-4 h-4 shrink-0') }} - Bewerbung abgelehnt - - {% else %} - - {{ icon('hourglass', 'w-4 h-4 shrink-0') }} - in Bearbeitung - - {% endif %} - {% elseif assignment.dispositions|length %} - - {{ icon('check', 'w-4 h-4 shrink-0') }} - eingeteilt - - {% elseif is_granted('APPLY', assignment) %} - - {{ icon('hand', 'w-4 h-4 shrink-0') }} - bewerben - - {% endif %} -
- {% set isBookmarked = teamer.bookmarks.contains(assignment) %} - - {{ icon('star', html_classes('w-5 h-5', { 'text-yellow-500': isBookmarked, 'text-gray-200': not isBookmarked } )) }} - -
- {% endfor %} -
- {{ knp_pagination_render(pagination, 'paginator/sliding_boxes.html.twig') }} {% endblock %} \ No newline at end of file