feat: all-season assignments
This commit is contained in:
1 parent
675cec1b69
commit
cf876ed224
119 files changed
+6194
-294
No files matched your search
@@ -93,7 +93,7 @@
|
||||
{% if recipients.eligibleCount > 0 %}
|
||||
<button type="button"
|
||||
class="btn"
|
||||
hx-post="{{ path('app_admin_teamer_mailing_confirm') }}"
|
||||
hx-post="{{ path('app_admin_teamer_mailing_confirm', { 'scope': scope }) }}"
|
||||
hx-include="closest form"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
@@ -105,7 +105,7 @@
|
||||
Vorschau an mich senden
|
||||
</button>
|
||||
{% if hasDraft %}
|
||||
<a href="{{ path('app_admin_teamer_mailing_discard') }}" class="btn btn--secondary">
|
||||
<a href="{{ path('app_admin_teamer_mailing_discard', { 'scope': scope }) }}" class="btn btn--secondary">
|
||||
Entwurf verwerfen
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@@ -41,4 +41,4 @@
|
||||
{# without a complete mail there is nothing to confirm, the button only leads back to the draft #}
|
||||
{% block button_confirm %}{{ sendable ? 'Jetzt senden' : 'Zurück zum Entwurf' }}{% endblock %}
|
||||
|
||||
{% block button_attribute %}{% if sendable %}hx-post="{{ path('app_admin_teamer_mailing_send') }}" hx-include="#mailing-confirm-fields"{% else %}{{ stimulus_action('modal', 'close') }}{% endif %}{% endblock %}
|
||||
{% block button_attribute %}{% if sendable %}hx-post="{{ path('app_admin_teamer_mailing_send', { 'scope': scope }) }}" hx-include="#mailing-confirm-fields"{% else %}{{ stimulus_action('modal', 'close') }}{% endif %}{% endblock %}
|
||||
@@ -20,6 +20,9 @@
|
||||
<div class="pb-4">
|
||||
{{ form_widget(form.status) }}
|
||||
</div>
|
||||
<div class="pb-4">
|
||||
{{ form_row(form.allSeason) }}
|
||||
</div>
|
||||
<div class="pb-4">
|
||||
{{ form_row(form.allowOverlappingApplications) }}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{# Swapped in place by its own route, so the id and the outerHTML swap have to stay. #}
|
||||
<div id="season-done-teamer" class="flex items-start space-x-2">
|
||||
<input type="checkbox"
|
||||
id="season-done-teamer-input"
|
||||
class="mt-1 text-primary"
|
||||
{{ seasonAssignment.doneTeamer ? 'checked' : '' }}
|
||||
hx-post="{{ path('app_administrative_season_assignment_done_teamer', { 'uuid': seasonAssignment.uuid }) }}"
|
||||
hx-target="#season-done-teamer"
|
||||
hx-swap="outerHTML">
|
||||
<label for="season-done-teamer-input">
|
||||
<span class="font-bold">Teamer:in erledigt</span>
|
||||
<span class="block text-sm text-gray-500">
|
||||
Reiner Vermerk, die wöchentliche Erinnerung läuft weiter,
|
||||
solange ein Punkt der Checkliste mit Rückgabe offen ist.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
{{ form_start(form) }}
|
||||
<div class="flex flex-col space-y-4 pb-8">
|
||||
<div class="grid lg:grid-cols-2 gap-x-8 gap-y-4">
|
||||
{{ form_row(form.teamer) }}
|
||||
{{ form_row(form.hotelCode) }}
|
||||
</div>
|
||||
<div class="grid lg:grid-cols-2 gap-x-8 gap-y-4">
|
||||
{{ form_row(form.dateFrom) }}
|
||||
{{ form_row(form.dateTo) }}
|
||||
</div>
|
||||
<div class="grid lg:grid-cols-2 gap-x-8 gap-y-4">
|
||||
{{ form_row(form.jobProfile) }}
|
||||
{{ form_row(form.remarks) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<button type="submit" class="btn">
|
||||
Speichern
|
||||
</button>
|
||||
<a href="{{ returnUrl }}" class="btn btn--secondary">
|
||||
Abbrechen
|
||||
</a>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
@@ -0,0 +1,63 @@
|
||||
{# One checklist row. Rendered inside the table on the detail page and again on its own by the
|
||||
toggle route, which swaps exactly this element - hence the id and the outerHTML swap. #}
|
||||
<tr id="season-task-{{ row.task.uuid }}" class="border-b border-gray-200">
|
||||
<td class="py-2 pr-4 align-top">
|
||||
<div class="font-bold">{{ row.definition.label }}</div>
|
||||
{% if row.definition.provides == constant('App\\Config\\SeasonTaskProvision::STANDARD') %}
|
||||
{% if row.document is not null %}
|
||||
<a href="{{ path('app_common_download', { 'uuid': row.document.uuid }) }}"
|
||||
class="text-sm underline">
|
||||
{{ row.document.displayName ?? row.document.originalFilename }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="text-sm text-gray-500">kein Dokument hinterlegt</div>
|
||||
{% endif %}
|
||||
{% elseif row.definition.provides == constant('App\\Config\\SeasonTaskProvision::INDIVIDUAL') %}
|
||||
<div class="text-sm text-gray-500">individuelles Dokument</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top whitespace-nowrap">
|
||||
{{ row.deadline1 is not null ? row.deadline1 | date('d.m.Y') : '-' }}
|
||||
{% if row.task.deadlineOverride1 is not null %}
|
||||
<span class="text-xs text-gray-500">(angepasst)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top whitespace-nowrap">
|
||||
{{ row.deadline2 is not null ? row.deadline2 | date('d.m.Y') : '-' }}
|
||||
{% if row.task.deadlineOverride2 is not null %}
|
||||
<span class="text-xs text-gray-500">(angepasst)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top text-center">
|
||||
<input type="checkbox"
|
||||
class="text-primary"
|
||||
{{ row.task.uploaded ? 'checked' : '' }}
|
||||
aria-label="Dokument erhalten"
|
||||
hx-post="{{ path('app_administrative_season_assignment_task_toggle', { 'uuid': row.task.uuid, 'field': 'uploaded' }) }}"
|
||||
hx-target="#season-task-{{ row.task.uuid }}"
|
||||
hx-swap="outerHTML">
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top text-center">
|
||||
<input type="checkbox"
|
||||
class="text-primary"
|
||||
{{ row.task.done ? 'checked' : '' }}
|
||||
aria-label="Erledigt"
|
||||
hx-post="{{ path('app_administrative_season_assignment_task_toggle', { 'uuid': row.task.uuid, 'field': 'done' }) }}"
|
||||
hx-target="#season-task-{{ row.task.uuid }}"
|
||||
hx-swap="outerHTML">
|
||||
{% if row.task.done and row.task.doneAt is not null %}
|
||||
<div class="text-xs text-gray-500 whitespace-nowrap">
|
||||
{{ row.task.doneAt | date('d.m.Y') }}{% if row.task.doneBy is not null %}, {{ row.task.doneBy }}{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="py-2 align-top text-right">
|
||||
<button type="button"
|
||||
class="text-sm underline"
|
||||
hx-get="{{ path('app_administrative_season_assignment_task_deadline', { 'uuid': row.task.uuid, 'r': return_url() }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
Fristen
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisoneinsatz anlegen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-8">
|
||||
Saisoneinsatz anlegen
|
||||
</h1>
|
||||
{% include 'administrative/season_assignment/_form.html.twig' %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,161 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisoneinsatz{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Saisoneinsatz
|
||||
<span class="block text-base font-normal text-gray-500">
|
||||
{{ seasonAssignment | season_assignment_label }}
|
||||
</span>
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<a href="{{ path('app_administrative_season_assignment_edit', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}" class="btn btn--small">
|
||||
bearbeiten
|
||||
</a>
|
||||
<a href="{{ returnUrl }}" class="btn btn--small btn--secondary">
|
||||
zurück
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% set teamer = seasonAssignment.teamer %}
|
||||
|
||||
<div class="flex flex-col lg:flex-row lg:space-x-8">
|
||||
<div class="flex-1 grid lg:grid-cols-2 gap-x-8 gap-y-4 pb-8">
|
||||
<div>
|
||||
<h4 class="font-bold pb-2">Teamer:in</h4>
|
||||
{% if teamer is not null %}
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}">
|
||||
{{ teamer }}
|
||||
</a>
|
||||
</li>
|
||||
{% if teamer.communication.email %}
|
||||
<li>
|
||||
<a href="mailto:{{ teamer.communication.email }}" class="flex items-center space-x-2">
|
||||
{{ icon('mail', 'w-4 h-4') }}
|
||||
<span class="flex-0 underline">{{ teamer.communication.email }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if teamer.communication.mobile %}
|
||||
<li>
|
||||
<a href="tel:{{ teamer.communication.mobile }}" class="flex items-center space-x-2">
|
||||
{{ icon('phone', 'w-4 h-4') }}
|
||||
<span class="flex-0 underline">{{ teamer.communication.mobile }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="text-gray-500">noch nicht zugewiesen</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<ul class="space-y-4">
|
||||
<li>
|
||||
<h4 class="font-bold pb-2">Jobprofil</h4>
|
||||
{{ seasonAssignment.jobProfile.name }}
|
||||
</li>
|
||||
<li>
|
||||
<h4 class="font-bold pb-2">Haus</h4>
|
||||
{{ seasonAssignment.hotelCode | house_label }}
|
||||
</li>
|
||||
<li>
|
||||
<h4 class="font-bold pb-2">Zeitraum</h4>
|
||||
{{ seasonAssignment.dateFrom | date('d.m.Y') }} - {{ seasonAssignment.dateTo | date('d.m.Y') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% if teamer is not null and teamer.photo is not null %}
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}">
|
||||
<img src="{{ asset(teamer.photo | upload_filename_full | imagine_filter('profile')) }}"
|
||||
class="w-48 h-auto border-2 border-primary"
|
||||
alt="{{ teamer.firstName }}">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="pb-8">
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Checkliste
|
||||
</h2>
|
||||
<div class="data-table-wrapper overflow-x-auto">
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr class="border-b-2 border-gray-300">
|
||||
<th class="py-2 pr-4">Dokument/ToDo</th>
|
||||
<th class="py-2 pr-4">Frist 1</th>
|
||||
<th class="py-2 pr-4">Frist 2</th>
|
||||
<th class="py-2 pr-4 text-center">erhalten</th>
|
||||
<th class="py-2 pr-4 text-center">erledigt</th>
|
||||
<th class="py-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in checklist %}
|
||||
{% include 'administrative/season_assignment/_task_row.html.twig' with { 'row': row } only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="pt-2 text-sm text-gray-500">
|
||||
Nur <em>erledigt</em> schließt einen Punkt ab und beendet die Erinnerungen.
|
||||
<em>erhalten</em> ist ein reiner Vermerk.
|
||||
</p>
|
||||
<div class="pt-4">
|
||||
{% include 'administrative/season_assignment/_done_teamer.html.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pb-8">
|
||||
<div class="flex items-center justify-between pb-2">
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Dokumente
|
||||
</h2>
|
||||
<button type="button"
|
||||
class="btn btn--small"
|
||||
hx-get="{{ path('app_administrative_season_assignment_document_upload', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
hochladen
|
||||
</button>
|
||||
</div>
|
||||
{% if seasonAssignment.documents | length > 0 %}
|
||||
<ul class="divide-y divide-gray-200">
|
||||
{% for document in seasonAssignment.documents %}
|
||||
<li class="flex items-center justify-between py-2">
|
||||
<div>
|
||||
<a href="{{ path('app_common_download', { 'uuid': document.uuid }) }}" class="underline">
|
||||
{{ document.originalFilename }}
|
||||
</a>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ document.typeLabel }}, hochgeladen am {{ document.createdAt | date('d.m.Y') }}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button"
|
||||
class="text-sm text-red-500"
|
||||
hx-get="{{ path('app_administrative_season_assignment_document_delete', { 'uuid': document.uuid, 'r': return_url() }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
löschen
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="text-gray-500">keine Dokumente vorhanden</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="pb-8">
|
||||
<h4 class="font-bold pb-2">Anmerkungen intern</h4>
|
||||
{% if seasonAssignment.remarks is not empty %}
|
||||
<div class="whitespace-pre-line">{{ seasonAssignment.remarks }}</div>
|
||||
{% else %}
|
||||
<div class="text-gray-500">-</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisoneinsatz bearbeiten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-8">
|
||||
Saisoneinsatz bearbeiten
|
||||
</h1>
|
||||
{% include 'administrative/season_assignment/_form.html.twig' %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,136 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisoneinsätze{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Saisoneinsätze
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<button type="button"
|
||||
class="{{ html_classes('btn btn--small', { 'btn--secondary': filterDto.active }) }}"
|
||||
title="Saisoneinsätze filtern"
|
||||
hx-get="{{ path('app_common_season_assignment_filter', { 'r': return_url() }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('filter', 'w-4 h-4 shrink-0') }}
|
||||
{% if filterDto.active %}
|
||||
<span class="whitespace-nowrap">{{ filterDto.activeFiltersCount }} Filter aktiv</span>
|
||||
{% else %}
|
||||
<span>Filter</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
{% if filterDto.active %}
|
||||
<a href="{{ path('app_common_season_assignment_filter_reset', { 'r': return_url() }) }}" class="btn btn--small btn--secondary">
|
||||
Reset
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Name', 'teamer.lastName') }}
|
||||
</th>
|
||||
<th>
|
||||
Vorname
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Haus', 'season_assignment.hotelCode') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Zeitraum', 'season_assignment.dateFrom') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Jobprofil', 'job_profile.name') }}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for seasonAssignment in pagination %}
|
||||
<tr>
|
||||
{# an assignment that has not been staffed yet still belongs in the list #}
|
||||
{% if seasonAssignment.teamer is not null %}
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_season_assignment_detail', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}">
|
||||
<span class="whitespace-nowrap">{{ seasonAssignment.teamer.lastName }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_season_assignment_detail', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}" class="whitespace-nowrap">
|
||||
{{ seasonAssignment.teamer.firstName }}
|
||||
</a>
|
||||
</td>
|
||||
{% else %}
|
||||
<td colspan="2">
|
||||
<a href="{{ path('app_administrative_season_assignment_detail', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}"
|
||||
class="whitespace-nowrap text-gray-500">
|
||||
noch nicht zugewiesen
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ seasonAssignment.hotelCode | house_label }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="whitespace-nowrap">
|
||||
{{ seasonAssignment.dateFrom | date('d.m.Y') }} - {{ seasonAssignment.dateTo | date('d.m.Y') }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ seasonAssignment.jobProfile.name }}
|
||||
</td>
|
||||
<td>
|
||||
<twig:DropDown>
|
||||
<a href="{{ path('app_administrative_season_assignment_detail', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}"
|
||||
class="text-gray-700 hover:text-primary block px-4 py-2 text-sm"
|
||||
role="menuitem"
|
||||
tabindex="-1">
|
||||
Details
|
||||
</a>
|
||||
<a href="{{ path('app_administrative_season_assignment_edit', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}"
|
||||
class="text-gray-700 hover:text-primary block px-4 py-2 text-sm"
|
||||
role="menuitem"
|
||||
tabindex="-1">
|
||||
bearbeiten
|
||||
</a>
|
||||
<button type="button"
|
||||
class="text-red-500 hover:text-primary block px-4 py-2 text-sm w-full text-left"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_administrative_season_assignment_delete', { 'uuid': seasonAssignment.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
löschen
|
||||
</button>
|
||||
</twig:DropDown>
|
||||
{% if seasonAssignment.deleted %}
|
||||
<div class="py-1 px-2 mt-2 text-xs bg-gray-500 text-white">
|
||||
gel. am {{ seasonAssignment.deletedAt | date('d.m.Y') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
Keine Daten...
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ knp_pagination_render(pagination) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ path('app_administrative_season_assignment_create', { 'r': return_url() }) }}" class="btn">
|
||||
Neu
|
||||
</a>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{% extends 'htmx_confirmation_modal.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
{% if seasonAssignment.teamer is not null %}
|
||||
Möchtest du den Saisoneinsatz von <em>{{ seasonAssignment.teamer }}</em>
|
||||
in <em>{{ seasonAssignment | season_assignment_label }}</em> wirklich löschen?
|
||||
{% else %}
|
||||
Möchtest du den noch nicht zugewiesenen Saisoneinsatz
|
||||
in <em>{{ seasonAssignment | season_assignment_label }}</em> wirklich löschen?
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends 'htmx_confirmation_modal.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
Möchtest du das Dokument <em>{{ document.originalFilename }}</em> wirklich löschen?
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Dokumente hochladen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set formAttr = stimulus_controller('form-upload-guard')|stimulus_action('form-upload-guard', 'guard', 'submit') %}
|
||||
{{ form_start(form, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' }|merge(formAttr.toArray()) }) }}
|
||||
<div class="pb-4">
|
||||
Lade hier die Dokumente hoch, die
|
||||
{% if seasonAssignment.teamer is not null %}<em>{{ seasonAssignment.teamer }}</em>{% else %}der:die Teamer:in{% endif %}
|
||||
für diesen Saisoneinsatz erhält. Sie sind nur für diese Person sichtbar.
|
||||
</div>
|
||||
<div class="pb-4">
|
||||
{% include '_partials/_upload_collection_form.html.twig' with {
|
||||
'endpoint_upload': path('_uploader_upload_season_document_out'),
|
||||
'max_filesize': 5,
|
||||
'max_files': 10,
|
||||
'accepted_files': 'image/jpg,image/jpeg,application/pdf',
|
||||
} %}
|
||||
</div>
|
||||
<button type="submit" class="btn" {{ stimulus_target('form-upload-guard', 'submit') }}>
|
||||
Dokumente speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Fristen: {{ task.task.label }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(form, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' } }) }}
|
||||
<div class="flex flex-col space-y-4 pb-8">
|
||||
<p class="text-sm text-gray-500">
|
||||
Ohne Eintrag gilt die Frist, die sich aus dem Beginn des Saisoneinsatzes ergibt.
|
||||
</p>
|
||||
{{ form_row(form.deadlineOverride1) }}
|
||||
{{ form_row(form.deadlineOverride2) }}
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisonteam-Dokumente{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-2">
|
||||
Saisonteam-Dokumente
|
||||
</h1>
|
||||
<p class="pb-8 text-gray-500">
|
||||
Auswahl der Standarddokumente für das Saisonteam.
|
||||
</p>
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ToDo</th>
|
||||
<th>Dokument</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
<td>{{ row.task.label }}</td>
|
||||
<td>
|
||||
{% if row.document is not null %}
|
||||
<a href="{{ path('app_common_download', { 'uuid': row.document.document.uuid }) }}" class="underline">
|
||||
{{ row.document.document.displayName ?? row.document.document.originalFilename }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-gray-500">kein Dokument hinterlegt</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center space-x-2 justify-end">
|
||||
{% if row.document is not null %}
|
||||
<button type="button"
|
||||
class="text-red-500"
|
||||
hx-get="{{ path('app_administrative_system_season_task_document_delete', { 'uuid': row.document.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('delete') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button"
|
||||
hx-get="{{ path('app_administrative_system_season_task_document_edit', { 'taskKey': row.task.value }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('edit') }}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends 'htmx_confirmation_modal.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
Möchtest du die Zuordnung für <em>{{ seasonTaskDocument.task ? seasonTaskDocument.task.label : seasonTaskDocument.taskKey }}</em>
|
||||
wirklich entfernen? Das Dokument selbst bleibt erhalten.
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Dokument für {{ task.label }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(form, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' } }) }}
|
||||
<div class="flex flex-col space-y-4 pb-8">
|
||||
{{ form_row(form.document) }}
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,36 @@
|
||||
{# Shared by both teamer lists. `scope` selects which list's filter the modal edits and
|
||||
which list a mailing resolves its recipients from; it is null on the general one. #}
|
||||
{% set scope = scope|default(null) %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<button type="button"
|
||||
class="{{ html_classes('btn btn--small', { 'btn--secondary': filterDto.active }) }}"
|
||||
title="Team filtern"
|
||||
hx-get="{{ path('app_common_teamer_filter', { 'r': return_url(), 'scope': scope }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('filter', 'w-4 h-4 shrink-0') }}
|
||||
{% if filterDto.active %}
|
||||
<span class="whitespace-nowrap">{{ filterDto.activeFiltersCount }} Filter aktiv</span>
|
||||
{% else %}
|
||||
<span>Filter</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
{% if filterDto.active %}
|
||||
<a href="{{ path('app_common_teamer_filter_reset', { 'r': return_url(), 'scope': scope }) }}" class="btn btn--small btn--secondary">
|
||||
Reset
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<a href="{{ path('app_admin_teamer_mailing', { 'r': return_url(), 'scope': scope }) }}"
|
||||
class="btn btn--small"
|
||||
title="Mail an die gefilterte Liste schreiben">
|
||||
{{ icon('mail', 'w-4 h-4 shrink-0') }}
|
||||
<span>Mailing</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,155 @@
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Name', 'teamer.lastName') }}
|
||||
</th>
|
||||
<th>
|
||||
Vorname
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Status', 'teamer.status') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'E-Mail', 'teamer.communication.email') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Letzter Login', 'user.lastLoginAt') }}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for teamer in pagination %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="flex flex-col items-center space-y-2">
|
||||
{% if teamer.photo %}
|
||||
<img src="{{ asset(teamer.photo | upload_filename_full | imagine_filter('thumbnail')) }}"
|
||||
class="object-cover w-12 h-12 rounded-full"
|
||||
alt="{{ teamer.firstName }}">
|
||||
{% else %}
|
||||
<div class="flex flex-col items-center justify-center w-12 h-12 border-2 border-gray-200 rounded-full">
|
||||
{{ icon('user', 'w-10 h-10 text-gray-200') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<twig:RatingStars rating="{{ teamer.averageRating }}" :teamer="teamer" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}">
|
||||
<span class="whitespace-nowrap">{{ teamer.lastName }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}" class="whitespace-nowrap">
|
||||
{{ teamer.firstName }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ teamer|teamer_status_label(showAllSeason|default(true)) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ teamer.communication.email }}
|
||||
</td>
|
||||
<td>
|
||||
{% if teamer.user is not null and teamer.user.lastLoginAt is not null %}
|
||||
{{ teamer.user.lastLoginAt | date('d.m.Y') }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center space-x-1 justify-end">
|
||||
{% if teamer.driverLicenseVerified %}
|
||||
{{ icon('car', 'w-5 h-5 text-green-500') }}
|
||||
{% endif %}
|
||||
{% if not teamer.viewed %}
|
||||
<button type="button"
|
||||
hx-post="{{ path('app_administrative_teamer_viewed', { 'uuid': teamer.uuid }) }}"
|
||||
title="Teamer als gesichtet markieren">
|
||||
{{ icon('check') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if teamer.deleted %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_restore_account', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend"
|
||||
title="Account wiederherstellen">
|
||||
{{ icon('refresh') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% elseif teamer.user is not null and teamer.user.disabled %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_enable_user', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('unlocked') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
class="text-red-500"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_disable_user', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('locked') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if is_granted('CAN_IMPERSONATE', teamer.user) %}
|
||||
<a href="{{ path('app_teamer_index', { '_switch_user': teamer.user.email }) }}" title="Als Teamer:in maskieren">
|
||||
{{ icon('mask') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
class="text-red-500"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_delete_account', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend"
|
||||
title="Account löschen">
|
||||
{{ icon('delete') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}" title="Teamer:innenprofil {{ teamer }}">
|
||||
{{ icon('user') }}
|
||||
</a>
|
||||
</div>
|
||||
{% if teamer.deleted %}
|
||||
<div class="py-1 px-2 mt-2 text-xs bg-gray-500 text-white">
|
||||
gel. am {{ teamer.deletedAt | date('d.m.Y') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if teamer.user is not null and teamer.user.disabled %}
|
||||
<div class="py-1 px-2 mt-2 text-xs bg-red-500 text-white">
|
||||
gesp. am {{ teamer.user.disabledAt | date('d.m.Y') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<td colspan="7">
|
||||
Keine Daten...
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ knp_pagination_render(pagination) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,192 +3,6 @@
|
||||
{% block title %}Teamübersicht{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Teamübersicht
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<button type="button"
|
||||
class="{{ html_classes('btn btn--small', { 'btn--secondary': filterDto.active }) }}"
|
||||
title="Team filtern"
|
||||
hx-get="{{ path('app_common_teamer_filter', { 'r': return_url() }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('filter', 'w-4 h-4 shrink-0') }}
|
||||
{% if filterDto.active %}
|
||||
<span class="whitespace-nowrap">{{ filterDto.activeFiltersCount }} Filter aktiv</span>
|
||||
{% else %}
|
||||
<span>Filter</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
{% if filterDto.active %}
|
||||
<a href="{{ path('app_common_teamer_filter_reset', { 'r': return_url() }) }}" class="btn btn--small btn--secondary">
|
||||
Reset
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<a href="{{ path('app_admin_teamer_mailing', { 'r': return_url() }) }}"
|
||||
class="btn btn--small"
|
||||
title="Mail an die gefilterte Liste schreiben">
|
||||
{{ icon('mail', 'w-4 h-4 shrink-0') }}
|
||||
<span>Mailing</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Name', 'teamer.lastName') }}
|
||||
</th>
|
||||
<th>
|
||||
Vorname
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Status', 'teamer.status') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'E-Mail', 'teamer.communication.email') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ knp_pagination_sortable(pagination, 'Letzter Login', 'user.lastLoginAt') }}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for teamer in pagination %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="flex flex-col items-center space-y-2">
|
||||
{% if teamer.photo %}
|
||||
<img src="{{ asset(teamer.photo | upload_filename_full | imagine_filter('profile')) }}"
|
||||
class="w-12 h-auto rounded-full"
|
||||
alt="{{ teamer.firstName }}">
|
||||
{% else %}
|
||||
<div class="flex flex-col items-center justify-center w-12 h-12 border-2 border-gray-200 rounded-full">
|
||||
{{ icon('user', 'w-10 h-10 text-gray-200') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<twig:RatingStars rating="{{ teamer.averageRating }}" :teamer="teamer" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}">
|
||||
<span class="whitespace-nowrap">{{ teamer.lastName }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}" class="whitespace-nowrap">
|
||||
{{ teamer.firstName }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ teamer|teamer_status_label }}
|
||||
</td>
|
||||
<td>
|
||||
{{ teamer.communication.email }}
|
||||
</td>
|
||||
<td>
|
||||
{% if teamer.user is not null and teamer.user.lastLoginAt is not null %}
|
||||
{{ teamer.user.lastLoginAt | date('d.m.Y') }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center space-x-1 justify-end">
|
||||
{% if teamer.driverLicenseVerified %}
|
||||
{{ icon('car', 'w-5 h-5 text-green-500') }}
|
||||
{% endif %}
|
||||
{% if not teamer.viewed %}
|
||||
<button type="button"
|
||||
hx-post="{{ path('app_administrative_teamer_viewed', { 'uuid': teamer.uuid }) }}"
|
||||
title="Teamer als gesichtet markieren">
|
||||
{{ icon('check') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if teamer.deleted %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_restore_account', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend"
|
||||
title="Account wiederherstellen">
|
||||
{{ icon('refresh') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% elseif teamer.user is not null and teamer.user.disabled %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_enable_user', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('unlocked') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
class="text-red-500"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_disable_user', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
{{ icon('locked') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if is_granted('CAN_IMPERSONATE', teamer.user) %}
|
||||
<a href="{{ path('app_teamer_index', { '_switch_user': teamer.user.email }) }}" title="Als Teamer:in maskieren">
|
||||
{{ icon('mask') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if is_granted('ROLE_TEAM_ADMIN') %}
|
||||
<button type="button"
|
||||
class="text-red-500"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
hx-get="{{ path('app_admin_teamer_delete_account', { 'uuid': teamer.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend"
|
||||
title="Account löschen">
|
||||
{{ icon('delete') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a href="{{ path('app_administrative_teamer_profile', { 'uuid': teamer.uuid, 'r': return_url() }) }}" title="Teamer:innenprofil {{ teamer }}">
|
||||
{{ icon('user') }}
|
||||
</a>
|
||||
</div>
|
||||
{% if teamer.deleted %}
|
||||
<div class="py-1 px-2 mt-2 text-xs bg-gray-500 text-white">
|
||||
gel. am {{ teamer.deletedAt | date('d.m.Y') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if teamer.user is not null and teamer.user.disabled %}
|
||||
<div class="py-1 px-2 mt-2 text-xs bg-red-500 text-white">
|
||||
gesp. am {{ teamer.user.disabledAt | date('d.m.Y') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<td colspan="7">
|
||||
Keine Daten...
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ knp_pagination_render(pagination) }}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'administrative/teamer/_header.html.twig' with { 'title': 'Teamübersicht' } %}
|
||||
{% include 'administrative/teamer/_table.html.twig' %}
|
||||
{% endblock %}
|
||||
@@ -244,6 +244,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if teamer.allSeason %}
|
||||
<div>
|
||||
<h2 class="text-lg font-bold pb-2">
|
||||
Saisoneinsätze
|
||||
</h2>
|
||||
<ul class="list-disc pl-4">
|
||||
{% for seasonAssignment in teamer.seasonAssignments|filter(seasonAssignment => not seasonAssignment.deleted) %}
|
||||
<li>
|
||||
<a href="{{ path('app_administrative_season_assignment_detail', { 'uuid': seasonAssignment.uuid, 'r': return_url() }) }}">
|
||||
{{ seasonAssignment.hotelCode | house_label }}
|
||||
</a>
|
||||
<span class="text-sm block">
|
||||
{{ seasonAssignment.dateFrom | date('d.m.Y') }} - {{ seasonAssignment.dateTo | date('d.m.Y') }},
|
||||
{{ seasonAssignment.jobProfile.name }}
|
||||
</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<span class="text-sm">Keine Daten</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends 'administrative/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saisonteamer{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'administrative/teamer/_header.html.twig' with {
|
||||
'title': 'Saisonteamer',
|
||||
'scope': constant('App\\Service\\Common\\TeamerFilterHandler::SCOPE_ALL_SEASON')
|
||||
} %}
|
||||
{# every row here is an all-season teamer, so the marker would only add noise #}
|
||||
{% include 'administrative/teamer/_table.html.twig' with { 'showAllSeason': false } %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Saisoneinsätze filtern{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(filterForm, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML'} }) }}
|
||||
<div class="flex flex-col space-y-2 pb-2">
|
||||
{{ form_row(filterForm.teamerName) }}
|
||||
{{ form_row(filterForm.hotelCodes) }}
|
||||
{{ form_row(filterForm.jobProfiles) }}
|
||||
{{ form_row(filterForm.includeDeleted) }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ form_widget(filterForm.apply, { 'attr': { 'class': 'btn' } }) }}
|
||||
{{ form_widget(filterForm.reset, { 'attr': { 'class': 'btn btn--secondary' } }) }}
|
||||
</div>
|
||||
{{ form_rest(filterForm) }}
|
||||
{{ form_end(filterForm) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends 'email/layout.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>
|
||||
Fristen für Saison-Teamer:innen-Dokumente erreicht
|
||||
</h1>
|
||||
<p>
|
||||
Für die folgenden Teamer:innen ist heute die Einreichungsfrist für Dokument erreicht:
|
||||
</p>
|
||||
{% for teamer, entries in violations %}
|
||||
<p>
|
||||
<strong>{{ teamer }}</strong><br>
|
||||
{% for entry in entries %}
|
||||
Frist {{ entry.level }}: {{ entry.label }}<br>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -200,6 +200,7 @@
|
||||
<input type="text"
|
||||
class="{{ attr.class }}"
|
||||
value="{{ form.vars.initial_label }}"
|
||||
{% if form.vars.placeholder %}placeholder="{{ form.vars.placeholder }}"{% endif %}
|
||||
{{ stimulus_target('autocomplete', 'input') }}>
|
||||
<ul class="autocomplete-result-list"></ul>
|
||||
<input type="hidden"
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
{% extends 'teamer/layout.html.twig' %}
|
||||
|
||||
{% block title %}Saison-Teamer:in{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="max-w-screen-md mx-auto">
|
||||
<h1 class="text-2xl font-bold pb-2">
|
||||
Saison-Teamer:in
|
||||
</h1>
|
||||
<p class="pb-8 text-gray-700">
|
||||
Für deinen Einsatz im Haus {{ seasonAssignment.hotelCode | house_label }}
|
||||
({{ seasonAssignment.dateFrom | date('d.m.Y') }} -
|
||||
{{ seasonAssignment.dateTo | date('d.m.Y') }})
|
||||
brauchen wir die folgenden Dokumente von dir.
|
||||
</p>
|
||||
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Allgemeine Dokumente
|
||||
</h2>
|
||||
|
||||
<dl class="divide-y divide-gray-100">
|
||||
{% for row in rows %}
|
||||
<div class="py-6 first:pt-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<dt class="font-bold leading-6 text-gray-900 sm:col-span-2">
|
||||
{{ row.definition.label }}
|
||||
<div class="text-sm font-normal text-gray-500">
|
||||
{% if row.deadline1 is not null %}
|
||||
bis {{ row.deadline1 | date('d.m.Y') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-6 sm:mt-0">
|
||||
{% if row.task.done %}
|
||||
<span class="text-green-600">erledigt</span>
|
||||
{% else %}
|
||||
<span class="text-gray-500">offen</span>
|
||||
{% endif %}
|
||||
{% if row.document is not null %}
|
||||
<a href="{{ path('app_common_download', { 'uuid': row.document.uuid }) }}" class="flex items-center space-x-1">
|
||||
{{ icon('download') }}
|
||||
<span class="flex-shrink-0">Vorlage herunterladen</span>
|
||||
</a>
|
||||
{% elseif row.definition.provides == constant('App\\Config\\SeasonTaskProvision::STANDARD') %}
|
||||
<div class="text-gray-500">kein Dokument hinterlegt</div>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
<div class="py-6">
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Persönliche Dokumente
|
||||
</h2>
|
||||
{% set personalDocuments = documents | filter(document => document.type == constant('App\\Entity\\Upload::TYPE_SEASON_DOCUMENT_OUT')) %}
|
||||
{% if personalDocuments | length > 0 %}
|
||||
<dl class="divide-y divide-gray-100">
|
||||
{% for document in personalDocuments %}
|
||||
<div class="py-2 flex items-center justify-between">
|
||||
<div>{{ document.displayName | default(document.originalFilename) }}</div>
|
||||
<a href="{{ path('app_common_download', { 'uuid': document.uuid }) }}" class="flex items-center space-x-1">
|
||||
{{ icon('download') }}
|
||||
<span class="flex-shrink-0">Download</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% else %}
|
||||
<div class="text-gray-500">Zurzeit liegen keine persönlichen Dokumente für dich bereit.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="py-6">
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Deine Uploads
|
||||
</h2>
|
||||
{% set returnedDocuments = documents | filter(document => document.type == constant('App\\Entity\\Upload::TYPE_SEASON_DOCUMENT_IN')) %}
|
||||
{% if returnedDocuments | length > 0 %}
|
||||
<dl class="divide-y divide-gray-100">
|
||||
{% for document in returnedDocuments %}
|
||||
<div class="py-2 flex items-center justify-between">
|
||||
<div>{{ document.originalFilename }}</div>
|
||||
<div class="text-sm text-gray-500">{{ document.createdAt | date('d.m.Y') }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% else %}
|
||||
<div class="text-gray-500">Du hast noch nichts hochgeladen.</div>
|
||||
{% endif %}
|
||||
|
||||
<button type="button"
|
||||
class="btn mt-4"
|
||||
hx-get="{{ path('app_teamer_season_upload', { 'uuid': seasonAssignment.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
Dokumente hochladen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Dokumente hochladen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set formAttr = stimulus_controller('form-upload-guard')|stimulus_action('form-upload-guard', 'guard', 'submit') %}
|
||||
{{ form_start(form, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' }|merge(formAttr.toArray()) }) }}
|
||||
<div class="pb-4">
|
||||
Lade hier die von dir unterschriebenen Dokumente hoch. Wir prüfen sie und haken die Punkte anschließend ab.
|
||||
</div>
|
||||
<div class="pb-4">
|
||||
{% include '_partials/_upload_collection_form.html.twig' with {
|
||||
'endpoint_upload': path('_uploader_upload_season_document_in'),
|
||||
'max_filesize': 5,
|
||||
'max_files': 10,
|
||||
'accepted_files': 'image/jpg,image/jpeg,application/pdf',
|
||||
} %}
|
||||
</div>
|
||||
<button type="submit" class="btn" {{ stimulus_target('form-upload-guard', 'submit') }}>
|
||||
Dokumente speichern
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user