WIP Impelement more stuff

This commit is contained in:
Björn Fromme committed 2023-10-04 15:10:16 +02:00
1 parent dec8feedf2
commit cc1ab396af
35 files changed
+928 -177

No files matched your search

+2 -2
View File
@@ -32,8 +32,8 @@ class Countries
return $countries;
}
public function get(int $id): ?Country
public function get(string $token): ?Country
{
return $this->getAll()[$id] ?? null;
return $this->getAll()[$token] ?? null;
}
}
+3 -3
View File
@@ -176,15 +176,15 @@ class ResponseParser
$countries = [];
foreach ($xml->xpath('laender/land') as $item) {
$id = (int) $item->attributes()['id'];
$token = (string) $item->attributes()['kuerzel'];
$country = new Country();
$country
->setId($id)
->setId((int) $item->attributes()['id'])
->setName((string) $item->attributes()['bezeichnung'])
->setToken((string) $item->attributes()['kuerzel'])
->setNationality((string) $item->attributes()['nationalitaet'])
;
$countries[$id] = $country;
$countries[$token] = $country;
}
$response = new BaseDataResponse();
@@ -0,0 +1,21 @@
<?php
namespace App\Controller\Admin\Teamer;
use App\Entity\Teamer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class AvailabilityController extends AbstractController
{
#[Route('/admin/teamer/availability/{uuid}', name: 'app_admin_teamer_availability')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Teamer $teamer): Response
{
return $this->render('admin/teamer/availability.html.twig', [
'teamer' => $teamer,
]);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Controller\Admin\Teamer;
use App\Entity\Teamer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class CrmSelectionsController extends AbstractController
{
#[Route('/admin/teamer/crm-selections/{uuid}', name: 'app_admin_teamer_crm_selections')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Teamer $teamer): Response
{
return $this->render('admin/teamer/crm_selections.html.twig', [
'teamer' => $teamer,
]);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Controller\Admin\Teamer;
use App\Entity\Teamer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class ProfileController extends AbstractController
{
#[Route('/admin/teamer/profile/{uuid}', name: 'app_admin_teamer_profile')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Teamer $teamer): Response
{
return $this->render('admin/teamer/profile.html.twig', [
'teamer' => $teamer,
]);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Controller\Admin\Teamer;
use App\Entity\Teamer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class RecentAssignmentsController extends AbstractController
{
#[Route('/admin/teamer/recent-assignments/{uuid}', name: 'app_admin_teamer_recent_assignments')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Teamer $teamer): Response
{
return $this->render('admin/teamer/recent_assignments.html.twig', [
'teamer' => $teamer,
]);
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin\Teamer;
use App\Entity\Teamer;
use App\Repository\TrainingRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class SkillsController extends AbstractController
{
public function __construct(private readonly TrainingRepository $trainingRepository)
{}
#[Route('/admin/teamer/skills/{uuid}', name: 'app_admin_teamer_skills')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Teamer $teamer): Response
{
$trainings = $this->trainingRepository->getList();
return $this->render('admin/teamer/skills.html.twig', [
'teamer' => $teamer,
'trainings' => $trainings,
]);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Controller\Admin\Teamer\TrainingAttendance;
use App\Entity\Teamer;
use App\Entity\Training;
use App\Entity\TrainingAttendance;
use App\Form\AbbreviatedDateType;
use App\Form\FeeType;
use App\Model\AjaxModalResponseDto;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class CreateController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger
) {
}
#[Route('/admin/teamer/skills/training-attendance/create/{training_id}/{teamer_id}', name: 'app_admin_teamer_skills_training_attendance_create')]
#[IsGranted('ROLE_ADMIN')]
public function index(
#[MapEntity(mapping: ['training_id' => 'id'])]
Training $training,
#[MapEntity(mapping: ['teamer_id' => 'id'])]
Teamer $teamer,
Request $request
): JsonResponse {
$response = new AjaxModalResponseDto();
$action = $this->generateUrl('app_admin_teamer_skills_training_attendance_create', [
'training_id' => $training->getId(),
'teamer_id' => $teamer->getId(),
]);
$attendance = new TrainingAttendance();
$attendance
->setTeamer($teamer)
->setTraining($training)
;
$form = $this
->createFormBuilder($attendance, ['action' => $action, 'ajax_submit' => true])
->add('date', AbbreviatedDateType::class, [
'label' => 'Datum',
])
->getForm()
;
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($attendance);
$this->entityManager->flush();
$this->addFlash('success', 'Die Teilnahme wurde hinzugefügt');
$this->logger->info('Create training attendance', [
'teamer' => $teamer->getUuid(),
'training' => $training->getName(),
]);
$redirectUrl = $this->generateUrl('app_admin_teamer_skills', ['uuid' => $teamer->getUuid()]);
$response->setCloseAndRedirect($redirectUrl);
} else {
$content = $this->renderView('admin/teamer/training_attendance/create.html.twig', [
'form' => $form
]);
$response->setContent($content);
}
return $this->json($response);
}
}
@@ -0,0 +1,37 @@
<?php
namespace App\Controller\Admin\Teamer\TrainingAttendance;
use App\Entity\TrainingAttendance;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class DeleteController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger
) {
}
#[Route('/admin/teamer/skills/training-attendance/delete/{uuid}', name: 'app_admin_teamer_skills_training_attendance_delete', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function index(TrainingAttendance $attendance): Response
{
$teamer = $attendance->getTeamer();
$this->entityManager->remove($attendance);
$this->entityManager->flush();
$this->addFlash('success', 'Die Teilnahme an der Fortbilundg wurde gelöscht');
$this->logger->info('Delete training attendance', [
'teamer' => $teamer->getUuid(),
]);
return $this->redirectToRoute('app_admin_teamer_skills', ['uuid' => $teamer->getUuid()]);
}
}
+15 -1
View File
@@ -2,17 +2,31 @@
namespace App\Controller\Teamer;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
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)
{}
#[Route('/teamer', name: 'app_teamer_index')]
#[IsGranted('ROLE_TEAMER')]
public function index(): Response
{
return $this->render('teamer/index.html.twig');
/** @var User $user */
$user = $this->getUser();
$teamer = $user->getTeamer();
// Validate teamer data to show missing data right away
$errors = $this->validator->validate($teamer, null, ['profile_preflight']);
return $this->render('teamer/index.html.twig', [
'errors' => $errors,
]);
}
}
@@ -35,11 +35,11 @@ class SkillsController extends AbstractController
foreach ($jobProfiles as $profile) {
$selectableJobProfiles[$profile->getId()] = true;
if (null !== $profile->getRequiredTraining() && false === $teamer->hasTraining($profile->getRequiredTraining())) {
if (null !== $profile->getRequiredTraining() && null === $teamer->getTrainingAttendance($profile->getRequiredTraining())) {
$selectableJobProfiles[$profile->getId()] = false;
}
foreach ($profile->getRequiredLicenses() as $license) {
if (false === $teamer->hasLicenseOfType($license)) {
if (null === $teamer->getLicenseOfType($license)) {
$selectableJobProfiles[$profile->getId()] = false;
}
}
-56
View File
@@ -1,56 +0,0 @@
<?php
namespace App\Entity;
use App\Entity\Traits\BlameableEntity;
use App\Entity\Traits\TimestampableEntity;
use App\Repository\PeriodRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PeriodRepository::class)]
class Period implements BlameableEntityInterface, TimestampableEntityInterface
{
use BlameableEntity;
use TimestampableEntity;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
private ?\DateTimeImmutable $dateFrom = null;
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
private ?\DateTimeImmutable $dateTo = null;
public function getId(): ?int
{
return $this->id;
}
public function getDateFrom(): ?\DateTimeImmutable
{
return $this->dateFrom;
}
public function setDateFrom(\DateTimeImmutable $dateFrom): static
{
$this->dateFrom = $dateFrom;
return $this;
}
public function getDateTo(): ?\DateTimeImmutable
{
return $this->dateTo;
}
public function setDateTo(\DateTimeImmutable $dateTo): static
{
$this->dateTo = $dateTo;
return $this;
}
}
+22 -8
View File
@@ -121,11 +121,11 @@ class Teamer implements TimestampableEntityInterface
#[ORM\Column]
private array $pickups = [];
#[ORM\Column(length: 255)]
#[ORM\Column(length: 255, nullable: true)]
#[Assert\NotBlank(message: 'Bitte gib deine Sprache(n) an', groups: ['profile', 'profile_preflight'])]
private ?string $language = null;
#[ORM\Column(length: 2)]
#[ORM\Column(length: 2, nullable: true)]
private ?string $size = null;
public function __construct()
@@ -143,6 +143,11 @@ class Teamer implements TimestampableEntityInterface
$this->customAvailabilities = new ArrayCollection();
}
public function __toString()
{
return $this->getFullName();
}
public function toPayload(): array
{
// Transform gender value
@@ -224,6 +229,15 @@ class Teamer implements TimestampableEntityInterface
return $this;
}
public function getFullName(bool $formal = false): string
{
if (true === $formal) {
return sprintf('%s, %s', $this->getLastName(), $this->getFirstName());
}
return sprintf('%s %s', $this->getFirstName(), $this->getLastName());
}
public function getGender(): ?string
{
return $this->gender;
@@ -494,15 +508,15 @@ class Teamer implements TimestampableEntityInterface
return $this;
}
public function hasTraining(Training $training): bool
public function getTrainingAttendance(Training $training): ?TrainingAttendance
{
foreach ($this->getTrainingAttendances() as $attendance) {
if ($attendance->getTraining() === $training) {
return true;
return $attendance;
}
}
return false;
return null;
}
/**
@@ -535,15 +549,15 @@ class Teamer implements TimestampableEntityInterface
return $this;
}
public function hasLicenseOfType(string $type): bool
public function getLicenseOfType(string $type): ?License
{
foreach ($this->getLicenses() as $license) {
if ($type === $license->getType()) {
return true;
return $license;
}
}
return false;
return null;
}
/**
+1 -1
View File
@@ -29,7 +29,7 @@ class TeamerJobProfileType extends AbstractType
$requirements[].= $training->getName();
}
foreach ($choice->getRequiredLicenses() as $license) {
$requirements[] = sprintf('Offizielle %slehrer*innenlizenz', ucfirst($license));
$requirements[] = sprintf('Offizielle %slehrer*innenlizenz', ucfirst(strtolower($license)));
}
if ($requirements) {
+52 -4
View File
@@ -6,9 +6,9 @@ use Knp\Menu\ItemInterface;
class AdminMenuBuilder extends AbstractMenuBuilder
{
public function createMainMenu(array $options): ItemInterface
private function getMainMenuItems(): array
{
$menuItems = [
return [
[
'route' => 'app_admin_index',
'title' => 'Dashboard',
@@ -34,7 +34,7 @@ class AdminMenuBuilder extends AbstractMenuBuilder
'title' => 'Teamerübersicht',
'icon' => 'users',
'hideChildren' => true,
'children' => [],
'children' => $this->getTeamerMenuItems(),
],
[
'route' => false,
@@ -86,8 +86,42 @@ class AdminMenuBuilder extends AbstractMenuBuilder
],
],
];
}
$menu = $this->createMenu($menuItems);
private function getTeamerMenuItems(): array
{
return [
[
'route' => 'app_admin_teamer_profile',
'title' => 'Stammdaten &amp; Foto',
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
],
[
'route' => 'app_admin_teamer_skills',
'title' => 'Jobprofile &amp; Skills',
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
],
[
'route' => 'app_admin_teamer_availability',
'title' => 'Verfügbarkeit',
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
],
[
'route' => 'app_admin_teamer_crm_selections',
'title' => 'Selektionsmerkmale',
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
],
[
'route' => 'app_admin_teamer_recent_assignments',
'title' => 'Vergangene Einsätze',
'routeParameters' => $this->getDefaultRouteParameters('uuid'),
],
];
}
public function createMainMenu(array $options): ItemInterface
{
$menu = $this->createMenu($this->getMainMenuItems());
$this->addDivider($menu);
@@ -100,4 +134,18 @@ class AdminMenuBuilder extends AbstractMenuBuilder
return $menu;
}
public function createTeamerMenu(array $options): ItemInterface
{
$menu = $this->createMenu($this->getTeamerMenuItems());
$this->addDivider($menu);
$menu->addChild('zurück zur Übersicht', [
'route' => 'app_admin_teamer_index',
'icon' => 'back',
]);
return $menu;
}
}
-66
View File
@@ -1,66 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\Period;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Period>
*
* @method Period|null find($id, $lockMode = null, $lockVersion = null)
* @method Period|null findOneBy(array $criteria, array $orderBy = null)
* @method Period[] findAll()
* @method Period[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PeriodRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Period::class);
}
public function save(Period $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(Period $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
// /**
// * @return Period[] Returns an array of Period objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('p.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Period
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}
+4
View File
@@ -15,6 +15,10 @@ 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('license_label', [AppRuntime::class, 'licenseLabel']),
new TwigFilter('teamer_status_label', [AppRuntime::class, 'teamerStatusLabel']),
new TwigFilter('bpn_country_label', [AppRuntime::class, 'bpnCountryLabel']),
new TwigFilter('bpn_pickup_label', [AppRuntime::class, 'bpnPickupLabel']),
];
}
+42
View File
@@ -2,6 +2,10 @@
namespace App\Twig;
use App\BusProNet\DataProvider\Countries;
use App\BusProNet\DataProvider\Pickups;
use App\BusProNet\Model\Country;
use App\Entity\Teamer;
use Carbon\Carbon;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Environment;
@@ -13,10 +17,48 @@ class AppRuntime implements RuntimeExtensionInterface
public function __construct(
private readonly RequestStack $requestStack,
private readonly IntlExtension $intlExtension,
private readonly Countries $countries,
private readonly Pickups $pickups,
private readonly string $environment
) {
}
public function teamerStatusLabel(string $status): string
{
if (Teamer::STATUS_NEW === $status) {
return 'Neuteamer';
}
return 'Bestandsteamer';
}
public function licenseLabel(string $type): string
{
return sprintf('Offizielle %slehrer*innenlizenz', ucfirst(strtolower($type)));
}
public function bpnCountryLabel(string $token, string $property = 'name'): string
{
$country = $this->countries->get($token);
if (null === $country) {
return 'unbekannt';
}
return 'nationality' === $property ? $country->getNationality() : $country->getName();
}
public function bpnPickupLabel(int $id): string
{
$pickup = $this->pickups->get($id);
if (null === $pickup) {
return 'unbekannt';
}
return $pickup->getCity();
}
public function fileIconFilter(Environment $environment, string $mimeType, ?string $classes = 'w-4 h-4'): string
{
$icon = match ($mimeType) {