feat: pickup selection depending on bus chaperon
This commit is contained in:
@@ -157,6 +157,9 @@
|
||||
<symbol id="icon-folder" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
||||
<path d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</symbol>
|
||||
<symbol id="icon-power" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.636 5.636a9 9 0 1 0 12.728 0M12 3v9" />
|
||||
</symbol>
|
||||
<symbol id="icon-bus" xmlns="http://www.w3.org/2000/svg" stroke-width="1.5" viewBox="0 0 24 24" stroke="currentColor" fill="none">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M6 17m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,33 @@
|
||||
<?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 Version20240110164517 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 application ADD pickup INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE disposition ADD pickup INT DEFAULT 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 pickup');
|
||||
$this->addSql('ALTER TABLE application DROP pickup');
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Controller\Traits\ReturnUrlTrait;
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Upload;
|
||||
use App\Entity\User;
|
||||
use App\Form\TeamerDispositionType;
|
||||
use App\Model\UploadSessionDto;
|
||||
use App\Service\Upload\UploadHandler;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -36,18 +37,21 @@ class DetailController extends AbstractController
|
||||
#[IsGranted('VIEW', subject: 'disposition')]
|
||||
public function index(Disposition $disposition, Request $request): Response
|
||||
{
|
||||
$isUploadContract = $this->workflow->can($disposition, 'upload_contract');
|
||||
$isUploadInvoice = $this->workflow->can($disposition, 'upload_invoice');
|
||||
|
||||
// Create dummy form without fields to generate POST requests
|
||||
$form = $this->createFormBuilder()->getForm();
|
||||
$form = $this->createForm(TeamerDispositionType::class, $disposition, ['enable_pickup' => $isUploadContract]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$uploadSession = $this->uploadHandler->getUploadSession();
|
||||
|
||||
if (0 < $uploadSession->getCount()) {
|
||||
if (true === $this->workflow->can($disposition, 'upload_contract')) {
|
||||
if (true === $isUploadContract) {
|
||||
$this->uploadContract($disposition, $uploadSession);
|
||||
}
|
||||
if (true === $this->workflow->can($disposition, 'upload_invoice')) {
|
||||
if (true === $isUploadInvoice) {
|
||||
$this->uploadInvoice($disposition, $uploadSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ class Application implements TimestampableEntityInterface
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
private ?string $requests = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $pickup = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
private ?string $status;
|
||||
|
||||
@@ -97,6 +100,18 @@ class Application implements TimestampableEntityInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPickup(): ?int
|
||||
{
|
||||
return $this->pickup;
|
||||
}
|
||||
|
||||
public function setPickup(?int $pickup): static
|
||||
{
|
||||
$this->pickup = $pickup;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): ?string
|
||||
{
|
||||
return $this->status;
|
||||
|
||||
@@ -42,6 +42,9 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
|
||||
#[ORM\ManyToOne(inversedBy: 'dispositions')]
|
||||
private ?Teamer $teamer;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $pickup = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
private ?string $remarks;
|
||||
|
||||
@@ -58,6 +61,7 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
|
||||
$this->status = static::STATUS_NEW;
|
||||
$this->assignment = $application->getAssignment();
|
||||
$this->teamer = $application->getTeamer();
|
||||
$this->pickup = $application->getPickup();
|
||||
$this->documents = new ArrayCollection();
|
||||
}
|
||||
|
||||
@@ -107,6 +111,18 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPickup(): ?int
|
||||
{
|
||||
return $this->pickup;
|
||||
}
|
||||
|
||||
public function setPickup(?int $pickup): static
|
||||
{
|
||||
$this->pickup = $pickup;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRemarks(): ?string
|
||||
{
|
||||
return $this->remarks;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Form;
|
||||
use App\Entity\Application;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
@@ -27,32 +28,36 @@ class TeamerApplicationType extends AbstractType
|
||||
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
/** @var Application $application */
|
||||
$application = $event->getData();
|
||||
$form = $event->getForm();
|
||||
|
||||
if (null === $application) {
|
||||
return;
|
||||
}
|
||||
|
||||
$assignment = $application->getAssignment();
|
||||
|
||||
if (0 === (int) $assignment->getPickup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$teamer = $application->getTeamer();
|
||||
|
||||
if (in_array($assignment->getPickup(), $teamer->getPickups())) {
|
||||
return;
|
||||
if (0 === (int) $assignment->getPickup()) {
|
||||
$pickupChoices = [];
|
||||
foreach ($assignment->getDestination()->getPickups() as $pickup) {
|
||||
$pickupChoices[$pickup['city']] = $pickup['busProId'];
|
||||
}
|
||||
ksort($pickupChoices);
|
||||
$form->add('pickup', ChoiceType::class, [
|
||||
'label' => 'Zustieg in',
|
||||
'choices' => $pickupChoices,
|
||||
]);
|
||||
} elseif (false === in_array($assignment->getPickup(), $teamer->getPickups())) {
|
||||
$form->add('confirmPickup', CheckboxType::class, [
|
||||
'label' => 'Ich bestätige den abweichenden Buszustieg',
|
||||
'mapped' => false,
|
||||
'constraints' => [
|
||||
new IsTrue([
|
||||
'message' => 'Deine Bestätigung ist erforderlich',
|
||||
]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$event->getForm()->add('confirmPickup', CheckboxType::class, [
|
||||
'label' => 'Ich bestätige den abweichenden Buszustieg',
|
||||
'mapped' => false,
|
||||
'constraints' => [
|
||||
new IsTrue([
|
||||
'message' => 'Deine Bestätigung ist erforderlich',
|
||||
]),
|
||||
],
|
||||
]);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Disposition;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TeamerDispositionType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
if (true === $options['enable_pickup']) {
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
/** @var Disposition $disposition */
|
||||
$disposition = $event->getData();
|
||||
$assignment = $disposition->getAssignment();
|
||||
|
||||
// Add pickup select field only to assignments that are not including bus chaperon
|
||||
if (0 === (int) $assignment->getPickup()) {
|
||||
$pickupChoices = [];
|
||||
foreach ($assignment->getDestination()->getPickups() as $pickup) {
|
||||
$pickupChoices[$pickup['city']] = $pickup['busProId'];
|
||||
}
|
||||
ksort($pickupChoices);
|
||||
$event->getForm()->add('pickup', ChoiceType::class, [
|
||||
'label' => 'Zustieg in',
|
||||
'choices' => $pickupChoices,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Disposition::class,
|
||||
'enable_pickup' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,16 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if application is defined and application.pickup is not null %}
|
||||
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<dt class="text-sm font-bold leading-6 text-gray-900">
|
||||
Gwählter Zustieg
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
||||
{{ application.pickup|bpn_pickup_label }}
|
||||
</dd>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<dt class="text-sm font-bold leading-6 text-gray-900">
|
||||
Du bekommst
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ul class="pb-8 divide-y divide-gray-200">
|
||||
{% if assignment.pickup and assignment.effectivePickupDate %}
|
||||
{% if assignment.pickup %}
|
||||
<li class="py-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="flex-1">Busbegleitung {{ assignment.pickup|bpn_pickup_label }} am {{ assignment.effectivePickupDate|date('d.m.Y') }}</span>
|
||||
@@ -22,5 +22,12 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="flex-1">Keine</span>
|
||||
{{ icon('check', 'w-5 h-5 text-green-500 shrink-0') }}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@@ -18,16 +18,19 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1/3">
|
||||
<th class="w-1/4">
|
||||
Name
|
||||
</th>
|
||||
<th class="w-1/3">
|
||||
<th class="w-1/4">
|
||||
Zustieg
|
||||
</th>
|
||||
<th>
|
||||
Wünsche
|
||||
</th>
|
||||
<th class="w-1/6">
|
||||
<th>
|
||||
Status
|
||||
</th>
|
||||
<th class="w-1/6"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -44,6 +47,10 @@
|
||||
<span>{{ application.teamer }}</span>
|
||||
{{ icon('info', 'w-4 h-4') }}
|
||||
</button>
|
||||
{% include '_partials/_rating_stars.html.twig' with { 'teamer': application.teamer } %}
|
||||
</td>
|
||||
<td>
|
||||
{{ application.pickup|bpn_pickup_label|default('-') }}
|
||||
</td>
|
||||
<td>
|
||||
{{ application.requests|nl2br|default('-') }}
|
||||
@@ -55,35 +62,36 @@
|
||||
<div class="flex items-center justify-end space-x-2">
|
||||
{% if is_granted('DISPOSE', application) %}
|
||||
<button type="button"
|
||||
class="btn btn--small"
|
||||
class="text-green-500"
|
||||
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
|
||||
{{ stimulus_action('modal-button', 'confirmation', null, {
|
||||
'title': 'Bist du sicher?',
|
||||
'content': 'Möchtest du ' ~ application.teamer ~ ' wirklich einteilen?',
|
||||
'target-url': path('app_admin_application_dispose', { 'uuid': application.uuid })
|
||||
}) }}>
|
||||
einteilen
|
||||
{{ icon('check') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if is_granted('STATUS', application) %}
|
||||
<button type="button" class="btn btn--secondary btn--small"
|
||||
<button type="button"
|
||||
class="text-primary"
|
||||
{{ stimulus_controller('modal-button', [], [], {'ajax-modal': '#ajax-modal'}) }}
|
||||
{{ stimulus_action('modal-button', 'ajax', null, {
|
||||
'title': 'Bewerbungsstatus bearbeiten',
|
||||
'url': path('app_admin_application_status', { 'uuid': application.uuid })
|
||||
}) }}>
|
||||
Status
|
||||
{{ icon('power') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if is_granted('DELETE', application) %}
|
||||
<button type="button" class="btn btn--secondary btn--small"
|
||||
<button type="button" class="text-red-500"
|
||||
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
|
||||
{{ stimulus_action('modal-button', 'confirmation', null, {
|
||||
'title': 'Bist du sicher?',
|
||||
'content': 'Möchtest du die Bewerbung von ' ~ application.teamer ~ ' wirklich löschen?',
|
||||
'target-url': path('app_admin_application_delete', { 'uuid': application.uuid })
|
||||
}) }}>
|
||||
löschen
|
||||
{{ icon('delete') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -108,16 +116,16 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1/3">
|
||||
<th class="w-1/4">
|
||||
Name
|
||||
</th>
|
||||
<th class="w-1/3">
|
||||
<th class="w-1/4">
|
||||
Anmerkungen
|
||||
</th>
|
||||
<th class="w-1/6">
|
||||
<th>
|
||||
Status
|
||||
</th>
|
||||
<th class="w-1/6"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -169,7 +177,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ returnUrl }}" class="underline">
|
||||
<a href="{{ returnUrl }}" class="btn">
|
||||
zurück
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
{% include '_partials/_assignment_requirements_info.html.twig' %}
|
||||
{{ form_start(form) }}
|
||||
<div class="flex flex-col space-y-4 pb-8">
|
||||
{% if form.pickup is defined %}
|
||||
{{ form_row(form.pickup) }}
|
||||
{% endif %}
|
||||
{% if form.confirmPickup is defined %}
|
||||
{{ form_row(form.confirmPickup) }}
|
||||
{% endif %}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="flex flex-col items-start space-y-4">
|
||||
{% 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.'
|
||||
'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') %}
|
||||
@@ -35,7 +35,7 @@
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% include '_partials/_infobox.html.twig' with {
|
||||
'message': 'Du hast dich am ' ~ application.createdAt|date('d.m.Y') ~ 'auf diesen Einsatz beworben.'
|
||||
'message': 'Du hast dich am ' ~ application.createdAt|date('d.m.Y') ~ ' auf diesen Einsatz beworben.'
|
||||
} %}
|
||||
<button type="button"
|
||||
class="btn btn--secondary"
|
||||
|
||||
@@ -66,24 +66,25 @@
|
||||
|
||||
{# New upload #}
|
||||
{% if workflow_can(disposition, 'upload_contract') %}
|
||||
<div class="pb-4">
|
||||
{{ form_start(form) }}
|
||||
<div class="flex flex-col space-y-4">
|
||||
<a href="{{ path('app_teamer_disposition_contractpdf', { 'uuid': disposition.uuid }) }}" target="_blank" class="inline-block">
|
||||
<img src="{{ asset('build/images/contract_thumb.jpg') }}" alt="Honorarvertrag" class="block w-32 h-auto shadow-md">
|
||||
</a>
|
||||
</div>
|
||||
<p class="pb-4">
|
||||
Bitte lade ihn unterschrieben hier wieder hoch:
|
||||
</p>
|
||||
{{ form_start(form) }}
|
||||
<div class="pb-4">
|
||||
<div>
|
||||
Bitte lade ihn unterschrieben hier wieder hoch:
|
||||
</div>
|
||||
{% include '_partials/_upload_collection_form.html.twig' with {
|
||||
'endpoint_upload': path('_uploader_upload_contract'),
|
||||
'accepted_files': 'image/jpg,image/jpeg,application/pdf',
|
||||
} %}
|
||||
{% if form.pickup is defined %}
|
||||
{{ form_row(form.pickup) }}
|
||||
{% endif %}
|
||||
<button type="submit" class="btn">
|
||||
Aktualisieren
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Aktualisieren
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% else %}
|
||||
@@ -99,24 +100,22 @@
|
||||
Honorarnote
|
||||
</h2>
|
||||
{% if workflow_can(disposition, 'upload_invoice') %}
|
||||
<div class="pb-4">
|
||||
{{ form_start(form) }}
|
||||
<div class="flex flex-col space-y-4">
|
||||
<a href="{{ path('app_teamer_disposition_invoicepdf', { 'uuid': disposition.uuid }) }}" target="_blank" class="inline-block">
|
||||
<img src="{{ asset('build/images/invoice_thumb.jpg') }}" alt="Honorarnote" class="block w-32 h-auto shadow-md">
|
||||
</a>
|
||||
</div>
|
||||
<p class="pb-4">
|
||||
Bitte lade sie ausgefüllt und unterschrieben hier wieder hoch:
|
||||
</p>
|
||||
{{ form_start(form) }}
|
||||
<div class="pb-4">
|
||||
<div>
|
||||
Bitte lade sie ausgefüllt und unterschrieben hier wieder hoch:
|
||||
</div>
|
||||
{% include '_partials/_upload_collection_form.html.twig' with {
|
||||
'endpoint_upload': path('_uploader_upload_invoice'),
|
||||
'accepted_files': 'image/jpg,image/jpeg,application/pdf',
|
||||
} %}
|
||||
<button type="submit" class="btn">
|
||||
Aktualisieren
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Aktualisieren
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% else %}
|
||||
|
||||
Reference in New Issue
Block a user